Add error messages in case of signature error
[oweals/opkg-lede.git] / libopkg / pkg_parse.c
1 /* pkg_parse.c - the opkg package management system
2
3    Steven M. Ayer
4    
5    Copyright (C) 2002 Compaq Computer Corporation
6
7    This program is free software; you can redistribute it and/or
8    modify it under the terms of the GNU General Public License as
9    published by the Free Software Foundation; either version 2, or (at
10    your option) any later version.
11
12    This program is distributed in the hope that it will be useful, but
13    WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15    General Public License for more details.
16 */
17
18 #include "includes.h"
19 #include <errno.h>
20 #include <ctype.h>
21    
22 #include "pkg.h"
23 #include "opkg_utils.h"
24 #include "pkg_parse.h"
25
26 int isGenericFieldType(char * type, char * line)
27 {
28     if(!strncmp(line, type, strlen(type)))
29         return 1;
30     return 0;
31 }
32
33 char * parseGenericFieldType(char * type, char * raw)
34 {
35     char * field_value = raw + (strlen(type) + 1);
36     return trim_alloc(field_value);
37 }
38
39 void parseStatus(pkg_t *pkg, char * raw)
40 {
41     char sw_str[64], sf_str[64], ss_str[64];
42
43     sscanf(raw, "Status: %s %s %s", sw_str, sf_str, ss_str);
44     pkg->state_want = pkg_state_want_from_str(sw_str);
45     pkg->state_flag = pkg_state_flag_from_str(sf_str);
46     pkg->state_status = pkg_state_status_from_str(ss_str);
47 }
48
49 char ** parseDependsString(char * raw, int * depends_count)
50 {
51     char ** depends = NULL;
52     int line_count = 0;
53     char buff[2048], * dest;
54
55     while(raw && *raw && !isspace(*raw)) {
56         raw++;
57     }
58
59     if(line_is_blank(raw)){
60         *depends_count = line_count;
61         return NULL;
62     }
63     while(raw && *raw){
64         depends = (char **)realloc(depends, sizeof(char *) * (line_count + 1));
65         
66         while(isspace(*raw)) raw++;
67
68         dest = buff;
69         while((*raw != ',') && *raw)
70             *dest++ = *raw++;
71
72         *dest = '\0';
73         depends[line_count] = trim_alloc(buff);
74         if(depends[line_count] ==NULL)
75            return NULL;
76         line_count++;
77         if(*raw == ',')
78             raw++;
79     }
80     *depends_count = line_count;
81     return depends;
82 }
83
84 void parseConffiles(pkg_t * pkg, char * raw)
85 {
86     char file_name[1048], md5sum[1048];  /* please tell me there aren't any longer that 1k */
87
88     if(!strncmp(raw, "Conffiles:", 10))
89         raw += strlen("Conffiles:");
90
91     while(*raw && (sscanf(raw, "%s%s", file_name, md5sum) == 2)){
92         conffile_list_append(&pkg->conffiles, file_name, md5sum);
93         /*      fprintf(stderr, "%s %s ", file_name, md5sum);*/
94         while (*raw && isspace(*raw)) {
95             raw++;
96         }
97         raw += strlen(file_name);
98         while (*raw && isspace(*raw)) {
99             raw++;
100         }
101         raw += strlen(md5sum);
102     }
103 }    
104
105 int parseVersion(pkg_t *pkg, char *raw)
106 {
107   char *colon, *eepochcolon;
108   char *hyphen;
109   unsigned long epoch;
110
111   if (!*raw) {
112       fprintf(stderr, "%s: ERROR: version string is empty", __FUNCTION__);
113       return EINVAL;
114   }
115
116   if (strncmp(raw, "Version:", 8) == 0) {
117       raw += 8;
118   }
119   while (*raw && isspace(*raw)) {
120       raw++;
121   }
122   
123   colon= strchr(raw,':');
124   if (colon) {
125     epoch= strtoul(raw,&eepochcolon,10);
126     if (colon != eepochcolon) {
127         fprintf(stderr, "%s: ERROR: epoch in version is not number", __FUNCTION__);
128         return EINVAL;
129     }
130     if (!*++colon) {
131         fprintf(stderr, "%s: ERROR: nothing after colon in version number", __FUNCTION__);
132         return EINVAL;
133     }
134     raw= colon;
135     pkg->epoch= epoch;
136   } else {
137     pkg->epoch= 0;
138   }
139
140   pkg->revision = "";
141
142   if (!pkg->version)
143   {
144   pkg->version= calloc(1, strlen(raw)+1);
145   if ( pkg->version == NULL ) {
146      fprintf(stderr, "%s: out of memory \n", __FUNCTION__);
147      return ENOMEM;
148   }
149   strcpy(pkg->version, raw);
150   }
151
152   hyphen= strrchr(pkg->version,'-');
153
154   if (hyphen) {
155     *hyphen++= 0;
156       pkg->revision = hyphen;
157   }
158
159   return 0;
160 }
161
162
163 /* This code is needed to insert in first position the keyword for the aligning bug */
164
165 int alterProvidesLine(char *raw, char *temp)
166 {
167
168
169   if (!*raw) {
170       fprintf(stderr, "%s: ERROR: Provides string is empty", __FUNCTION__);
171       return -EINVAL;
172   }
173
174   if ( temp == NULL ) {
175      fprintf(stderr, "%s: out of memory \n", __FUNCTION__);
176      return -ENOMEM;
177   }
178
179   if (strncmp(raw, "Provides:", 9) == 0) {
180       raw += 9;
181   }
182   while (*raw && isspace(*raw)) {
183       raw++;
184   }      
185   
186   snprintf ( temp, 35, "Provides: opkg_internal_use_only, ");           /* First part of the line */
187   while (*raw) {
188      strncat( temp, raw++, 1);
189   }
190   return 0;
191  
192 }
193
194 /* Some random thoughts from Carl:
195
196    This function could be considerably simplified if we just kept
197    an array of all the generic string-valued field names, and looped
198    through those looking for a match. Also, these fields could perhaps
199    be stored in the package as an array as well, (or, probably better,
200    as an nv_pair_list_t).
201
202    Fields which require special parsing or storage, (such as Depends:
203    and Status:) could be handled as they are now. 
204 */
205 /* XXX: FEATURE: The Suggests: field needs to be changed from a string
206    to a dependency list. And, since we already have
207    Depends/Pre-Depends and need to add Conflicts, Recommends, and
208    Enhances, perhaps we could generalize all of these and save some
209    code duplication.
210 */
211 int pkg_parse_raw(pkg_t *pkg, char ***raw, pkg_src_t *src, pkg_dest_t *dest)
212 {
213     int reading_conffiles, reading_description;
214     int pkg_false_provides=1;
215     char ** lines;
216     char * provide=NULL;
217
218     pkg->src = src;
219     pkg->dest = dest;
220
221     reading_conffiles = reading_description = 0;
222
223     for (lines = *raw; *lines; lines++) {
224         /*      fprintf(stderr, "PARSING %s\n", *lines);*/
225         switch (**lines) {
226         case 'P':
227             if(isGenericFieldType("Package:", *lines)) 
228                 pkg->name = parseGenericFieldType("Package", *lines);
229             else if(isGenericFieldType("Priority:", *lines))
230                 pkg->priority = parseGenericFieldType("Priority", *lines);
231             else if(isGenericFieldType("Provides", *lines)){
232 /* Here we add the internal_use to align the off by one problem between provides_str and provides */
233                 provide = (char * ) calloc(1, strlen(*lines)+ 35 ); /* Preparing the space for the new opkg_internal_use_only */
234                 if ( alterProvidesLine(*lines,provide) ){
235                     return EINVAL;
236                 }
237                 pkg->provides_str = parseDependsString( provide, &pkg->provides_count);
238 /* Let's try to hack a bit here.
239    The idea is that if a package has no Provides, we would add one generic, to permit the check of dependencies
240    in alot of other places. We will remove it before writing down the status database */
241                 pkg_false_provides=0;
242                 free(provide);
243             } 
244             else if(isGenericFieldType("Pre-Depends", *lines))
245                 pkg->pre_depends_str = parseDependsString(*lines, &pkg->pre_depends_count);
246             break;
247
248         case 'A':
249             if(isGenericFieldType("Architecture:", *lines))
250                 pkg->architecture = parseGenericFieldType("Architecture", *lines);
251             else if(isGenericFieldType("Auto-Installed:", *lines)) {
252                 char *auto_installed_value;
253                 auto_installed_value = parseGenericFieldType("Auto-Installed:", *lines);
254                 if (strcmp(auto_installed_value, "yes") == 0) {
255                     pkg->auto_installed = 1;
256                 }
257                 free(auto_installed_value);
258             }
259             break;
260
261         case 'F':
262             if(isGenericFieldType("Filename:", *lines))
263                 pkg->filename = parseGenericFieldType("Filename", *lines);
264             break;
265
266         case 'S':
267             if(isGenericFieldType("Section:", *lines))
268                 pkg->section = parseGenericFieldType("Section", *lines);
269 #ifdef HAVE_SHA256
270             else if(isGenericFieldType("SHA256sum:", *lines))
271                 pkg->sha256sum = parseGenericFieldType("SHA256sum", *lines);
272 #endif
273             else if(isGenericFieldType("Size:", *lines))
274                 pkg->size = parseGenericFieldType("Size", *lines);
275             else if(isGenericFieldType("Source:", *lines))
276                 pkg->source = parseGenericFieldType("Source", *lines);
277             else if(isGenericFieldType("Status", *lines))
278                 parseStatus(pkg, *lines);
279             else if(isGenericFieldType("Suggests", *lines))
280                 pkg->suggests_str = parseDependsString(*lines, &pkg->suggests_count);
281             break;
282
283         case 'T':
284             if(isGenericFieldType("Tags:", *lines))
285                 pkg->tags = parseGenericFieldType("Tags", *lines);
286             break;
287
288         case 'M':
289             if(isGenericFieldType("MD5sum:", *lines))
290                 pkg->md5sum = parseGenericFieldType("MD5sum", *lines);
291             /* The old opkg wrote out status files with the wrong case for MD5sum,
292                 let's parse it either way */
293             else if(isGenericFieldType("MD5Sum:", *lines))
294                 pkg->md5sum = parseGenericFieldType("MD5Sum", *lines);
295             else if(isGenericFieldType("Maintainer", *lines))
296                 pkg->maintainer = parseGenericFieldType("Maintainer", *lines);
297             break;
298
299         case 'I':
300             if(isGenericFieldType("Installed-Size:", *lines))
301                 pkg->installed_size = parseGenericFieldType("Installed-Size", *lines);
302             else if(isGenericFieldType("Installed-Time:", *lines)) {
303                 char *time_str = parseGenericFieldType("Installed-Time", *lines);
304                 pkg->installed_time = strtoul(time_str, NULL, 0);
305                 free (time_str);
306             }       
307             break;
308
309         case 'E':
310             if(isGenericFieldType("Essential:", *lines)) {
311                 char *essential_value;
312                 essential_value = parseGenericFieldType("Essential", *lines);
313                 if (strcmp(essential_value, "yes") == 0) {
314                     pkg->essential = 1;
315                 }
316                 free(essential_value);
317             }
318             break;
319
320         case 'V':
321             if(isGenericFieldType("Version", *lines))
322                 parseVersion(pkg, *lines);
323             break;
324
325         case 'C':
326             if(isGenericFieldType("Conffiles", *lines)){
327                 parseConffiles(pkg, *lines);
328                 reading_conffiles = 1;
329             }
330             else if(isGenericFieldType("Conflicts", *lines))
331                 pkg->conflicts_str = parseDependsString(*lines, &pkg->conflicts_count);
332             break;
333
334         case 'D':
335             if(isGenericFieldType("Description", *lines)) {
336                 pkg->description = parseGenericFieldType("Description", *lines);
337                 reading_conffiles = 0;
338                 reading_description = 1;
339             }
340             else if(isGenericFieldType("Depends", *lines))
341                 pkg->depends_str = parseDependsString(*lines, &pkg->depends_count);
342             break;
343
344         case 'R':
345             if(isGenericFieldType("Recommends", *lines))
346                 pkg->recommends_str = parseDependsString(*lines, &pkg->recommends_count);
347             else if(isGenericFieldType("Replaces", *lines))
348                 pkg->replaces_str = parseDependsString(*lines, &pkg->replaces_count);
349             
350             break;
351
352         case ' ':
353             if(reading_description) {
354                 /* we already know it's not blank, so the rest of description */      
355                 pkg->description = realloc(pkg->description,
356                                            strlen(pkg->description)
357                                            + 1 + strlen(*lines) + 1);
358                 strcat(pkg->description, "\n");
359                 strcat(pkg->description, (*lines));
360             }
361             else if(reading_conffiles)
362                 parseConffiles(pkg, *lines);
363                 
364             break;
365
366         default:
367             if(line_is_blank(*lines)) {
368                 lines++;
369                 goto out;
370             }
371         }
372     }
373 out:;
374     
375     *raw = lines;
376 /* If the opk has not a Provides line, we insert our false line */ 
377     if ( pkg_false_provides==1)
378     {
379        pkg->provides_count = 1;
380        pkg->provides_str = calloc (1, sizeof (char*));
381        pkg->provides_str[0] = strdup ("opkg_internal_use_only");
382     }
383
384     if (pkg->name) {
385         return 0;
386     } else {
387         return EINVAL;
388     }
389 }
390
391 int pkg_valorize_other_field(pkg_t *pkg, char ***raw)
392 {
393     char ** lines;
394
395     for (lines = *raw; *lines; lines++) {
396         if(isGenericFieldType("Essential:", *lines)) {
397             char *essential_value;
398             essential_value = parseGenericFieldType("Essential", *lines);
399             if (strcmp(essential_value, "yes") == 0) {
400                 pkg->essential = 1;
401             }
402             free(essential_value);
403         }
404     }
405     *raw = lines;
406
407     return 0;
408 }