/* This file is part of "reprepro" * Copyright (C) 2003,2004,2005,2006,2007,2008,2009 Bernhard R. Link * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02111-1301 USA */ #include #include #include #include #include #include #include #include #include #include #include "error.h" #include "mprintf.h" #include "strlist.h" #include "chunks.h" #include "sources.h" #include "names.h" #include "dpkgversions.h" #include "override.h" #include "tracking.h" #include "signature.h" /* split a " " into md5sum and filename */ static retvalue calc_parsefileline(const char *fileline, /*@out@*/char **filename) { const char *p, *fn, *fnend; char *filen; assert( fileline != NULL ); if( *fileline == '\0' ) return RET_NOTHING; /* the md5sums begins after the (perhaps) heading spaces ... */ p = fileline; while( *p != '\0' && (*p == ' ' || *p == '\t') ) p++; if( *p == '\0' ) return RET_NOTHING; /* ... and ends with the following spaces. */ while( *p != '\0' && !(*p == ' ' || *p == '\t') ) p++; if( *p == '\0' ) { fprintf(stderr, "Expecting more data after md5sum!\n"); return RET_ERROR; } /* Then the size of the file is expected: */ while( (*p == ' ' || *p == '\t') ) p++; while( *p !='\0' && !(*p == ' ' || *p == '\t') ) p++; if( *p == '\0' ) { fprintf(stderr, "Expecting more data after size!\n"); return RET_ERROR; } /* Then the filename */ fn = p; while( (*fn == ' ' || *fn == '\t') ) fn++; fnend = fn; while( *fnend != '\0' && !(*fnend == ' ' || *fnend == '\t') ) fnend++; filen = strndup(fn, fnend-fn); if( FAILEDTOALLOC(filen) ) return RET_ERROR_OOM; *filename = filen; return RET_OK; } static retvalue getBasenames(const struct strlist *filelines,/*@out@*/struct strlist *basenames) { int i; retvalue r; assert( filelines != NULL && basenames != NULL ); r = strlist_init_n(filelines->count,basenames); if( RET_WAS_ERROR(r) ) return r; r = RET_NOTHING; for( i = 0 ; i < filelines->count ; i++ ) { char *basefilename IFSTUPIDCC(=NULL); const char *fileline = filelines->values[i]; r = calc_parsefileline(fileline, &basefilename); if( r == RET_NOTHING ) { fprintf(stderr, "Malformed Files: line '%s'!\n", fileline); r = RET_ERROR; } if( RET_WAS_ERROR(r) ) break; r = strlist_add(basenames, basefilename); if( RET_WAS_ERROR(r) ) { break; } r = RET_OK; } if( RET_WAS_ERROR(r) ) { strlist_done(basenames); } else { assert( filelines->count == basenames->count ); } return r; } retvalue sources_getversion(const char *control, char **version) { retvalue r; r = chunk_getvalue(control,"Version",version); if( RET_WAS_ERROR(r) ) return r; if( r == RET_NOTHING ) { fprintf(stderr, "Missing 'Version' field in chunk:'%s'\n", control); return RET_ERROR; } return r; } retvalue sources_getarchitecture(UNUSED(const char *chunk), architecture_t *architecture_p) { *architecture_p = architecture_source; return RET_OK; } retvalue sources_getinstalldata(const struct target *t, const char *packagename, UNUSED(const char *version), architecture_t architecture, const char *chunk, char **control, struct strlist *filekeys, struct checksumsarray *origfiles) { retvalue r; char *origdirectory, *directory, *mychunk; struct strlist myfilekeys; struct strlist filelines[cs_hashCOUNT]; struct checksumsarray files; enum checksumtype cs; assert( architecture == architecture_source ); for( cs = cs_md5sum ; cs < cs_hashCOUNT ; cs++ ) { assert( source_checksum_names[cs] != NULL ); r = chunk_getextralinelist(chunk, source_checksum_names[cs], &filelines[cs]); if( r == RET_NOTHING ) { if( cs == cs_md5sum ) { fprintf(stderr, "Missing 'Files' entry in '%s'!\n", chunk); r = RET_ERROR; } else strlist_init(&filelines[cs]); } if( RET_WAS_ERROR(r) ) { while( cs-- > cs_md5sum ) { strlist_done(&filelines[cs]); } return r; } } r = checksumsarray_parse(&files, filelines, packagename); for( cs = cs_md5sum ; cs < cs_hashCOUNT ; cs++ ) { strlist_done(&filelines[cs]); } if( RET_WAS_ERROR(r) ) return r; r = chunk_getvalue(chunk, "Directory", &origdirectory); if( r == RET_NOTHING ) { /* Flat repositories can come without this, TODO: add warnings in other cases fprintf(stderr,"Missing 'Directory' entry in '%s'!\n",chunk); r = RET_ERROR; */ origdirectory = strdup("."); if( FAILEDTOALLOC(origdirectory) ) r = RET_ERROR_OOM; } if( RET_WAS_ERROR(r) ) { checksumsarray_done(&files); return r; } r = propersourcename(packagename); assert( r != RET_NOTHING ); if( RET_IS_OK(r) ) r = properfilenames(&files.names); if( RET_WAS_ERROR(r) ) { fprintf(stderr,"Forbidden characters in source package '%s'!\n", packagename); free(origdirectory); checksumsarray_done(&files); return r; } directory = calc_sourcedir(t->component_atom, packagename); if( directory == NULL ) r = RET_ERROR_OOM; else r = calc_dirconcats(directory, &files.names, &myfilekeys); if( RET_WAS_ERROR(r) ) { free(directory); free(origdirectory); checksumsarray_done(&files); return r; } r = calc_inplacedirconcats(origdirectory, &files.names); free(origdirectory); if( !RET_WAS_ERROR(r) ) { mychunk = chunk_replacefield(chunk, "Directory", directory, true); if( mychunk == NULL ) r = RET_ERROR_OOM; } free(directory); if( RET_WAS_ERROR(r) ) { strlist_done(&myfilekeys); checksumsarray_done(&files); return r; } *control = mychunk; strlist_move(filekeys, &myfilekeys); checksumsarray_move(origfiles, &files); return RET_OK; } retvalue sources_getfilekeys(const char *chunk, struct strlist *filekeys) { char *origdirectory; struct strlist basenames; retvalue r; struct strlist filelines; /* Read the directory given there */ r = chunk_getvalue(chunk, "Directory", &origdirectory); if( r == RET_NOTHING ) { //TODO: check if it is even text and do not print //of looking binary?? fprintf(stderr, "Does not look like source control: '%s'\n", chunk); return RET_ERROR; } if( RET_WAS_ERROR(r) ) return r; r = chunk_getextralinelist(chunk, "Files", &filelines); if( r == RET_NOTHING ) { //TODO: check if it is even text and do not print //of looking binary?? fprintf(stderr, "Does not look like source control: '%s'\n", chunk); r = RET_ERROR; } if( RET_WAS_ERROR(r) ) { free(origdirectory); return r; } r = getBasenames(&filelines, &basenames); strlist_done(&filelines); if( RET_WAS_ERROR(r) ) { free(origdirectory); return r; } r = calc_dirconcats(origdirectory, &basenames, filekeys); free(origdirectory); strlist_done(&basenames); return r; } retvalue sources_getchecksums(const char *chunk, struct checksumsarray *out) { char *origdirectory; struct checksumsarray a; retvalue r; struct strlist filelines[cs_hashCOUNT]; enum checksumtype cs; /* Read the directory given there */ r = chunk_getvalue(chunk, "Directory", &origdirectory); if( !RET_IS_OK(r) ) return r; for( cs = cs_md5sum ; cs < cs_hashCOUNT ; cs++ ) { assert( source_checksum_names[cs] != NULL ); r = chunk_getextralinelist(chunk, source_checksum_names[cs], &filelines[cs]); if( r == RET_NOTHING ) { if( cs == cs_md5sum ) { fprintf(stderr, "Missing 'Files' entry in '%s'!\n", chunk); r = RET_ERROR; } else strlist_init(&filelines[cs]); } if( RET_WAS_ERROR(r) ) { while( cs-- > cs_md5sum ) { strlist_done(&filelines[cs]); } free(origdirectory); return r; } } r = checksumsarray_parse(&a, filelines, "source chunk"); for( cs = cs_md5sum ; cs < cs_hashCOUNT ; cs++ ) { strlist_done(&filelines[cs]); } if( RET_WAS_ERROR(r) ) { free(origdirectory); return r; } r = calc_inplacedirconcats(origdirectory, &a.names); free(origdirectory); if( RET_WAS_ERROR(r) ) { checksumsarray_done(&a); return r; } checksumsarray_move(out, &a); return RET_OK; } retvalue sources_doreoverride(const struct distribution *distribution,const char *packagename,const char *controlchunk,/*@out@*/char **newcontrolchunk) { const struct overrideinfo *o; struct fieldtoadd *fields; char *newchunk; retvalue r; if( interrupted() ) return RET_ERROR_INTERRUPTED; o = override_search(distribution->overrides.dsc, packagename); if( o == NULL ) return RET_NOTHING; r = override_allreplacefields(o, &fields); if( RET_WAS_ERROR(r) ) return r; newchunk = chunk_replacefields(controlchunk, fields, "Directory", true); addfield_free(fields); if( newchunk == NULL ) return RET_ERROR_OOM; *newcontrolchunk = newchunk; return RET_OK; } retvalue sources_retrack(const char *sourcename, const char *chunk, trackingdb tracks, struct database *database) { retvalue r; char *sourceversion; struct trackedpackage *pkg; struct strlist filekeys; int i; //TODO: elliminate duplicate code! assert(sourcename!=NULL); if( interrupted() ) return RET_ERROR_INTERRUPTED; r = chunk_getvalue(chunk,"Version",&sourceversion); if( r == RET_NOTHING ) { fprintf(stderr, "Missing 'Version' field in chunk:'%s'\n", chunk); r = RET_ERROR; } if( RET_WAS_ERROR(r) ) { return r; } r = sources_getfilekeys(chunk, &filekeys); if( r == RET_NOTHING ) { fprintf(stderr, "Malformed source control:'%s'\n", chunk); r = RET_ERROR; } if( RET_WAS_ERROR(r) ) { free(sourceversion); return r; } r = tracking_getornew(tracks,sourcename,sourceversion,&pkg); free(sourceversion); if( RET_WAS_ERROR(r) ) { strlist_done(&filekeys); return r; } // TODO: error handling is suboptimal here. // is there a way to again remove old additions (esp. references) // where something fails? for( i = 0 ; i < filekeys.count ; i++ ) { r = trackedpackage_addfilekey(tracks, pkg, ft_SOURCE, filekeys.values[i], true, database); filekeys.values[i] = NULL; if( RET_WAS_ERROR(r) ) { strlist_done(&filekeys); trackedpackage_free(pkg); return r; } } strlist_done(&filekeys); return tracking_save(tracks, pkg); } retvalue sources_getsourceandversion(const char *chunk, const char *packagename, char **source, char **version) { retvalue r; char *sourceversion; char *sourcename; //TODO: elliminate duplicate code! assert(packagename!=NULL); r = chunk_getvalue(chunk,"Version",&sourceversion); if( r == RET_NOTHING ) { fprintf(stderr, "Missing 'Version' field in chunk:'%s'\n", chunk); r = RET_ERROR; } if( RET_WAS_ERROR(r) ) { return r; } sourcename = strdup(packagename); if( sourcename == NULL ) { free(sourceversion); return RET_ERROR_OOM; } *source = sourcename; *version = sourceversion; return RET_OK; } /****************************************************************/ static inline retvalue getvalue(const char *filename,const char *chunk,const char *field,char **value) { retvalue r; r = chunk_getvalue(chunk,field,value); if( r == RET_NOTHING ) { fprintf(stderr,"Missing '%s' field in %s!\n",field,filename); r = RET_ERROR; } return r; } static inline retvalue checkvalue(const char *filename,const char *chunk,const char *field) { retvalue r; r = chunk_checkfield(chunk,field); if( r == RET_NOTHING ) { fprintf(stderr,"Cannot find '%s' field in %s!\n",field,filename); r = RET_ERROR; } return r; } static inline retvalue getvalue_n(const char *chunk,const char *field,char **value) { retvalue r; r = chunk_getvalue(chunk,field,value); if( r == RET_NOTHING ) { *value = NULL; } return r; } retvalue sources_readdsc(struct dsc_headers *dsc, const char *filename, const char *filenametoshow, bool *broken) { retvalue r; struct strlist filelines[cs_hashCOUNT]; enum checksumtype cs; r = signature_readsignedchunk(filename, filenametoshow, &dsc->control, NULL, broken); if( RET_WAS_ERROR(r) ) { return r; } if( verbose > 100 ) { fprintf(stderr, "Extracted control chunk from '%s': '%s'\n", filenametoshow, dsc->control); } /* first look for fields that should be there */ r = chunk_getname(dsc->control, "Source", &dsc->name, false); if( r == RET_NOTHING ) { fprintf(stderr, "Missing 'Source' field in %s!\n", filenametoshow); return RET_ERROR; } if( RET_WAS_ERROR(r) ) return r; /* This is needed and cannot be ignored unless * sources_complete is changed to not need it */ r = checkvalue(filenametoshow, dsc->control, "Format"); if( RET_WAS_ERROR(r) ) return r; r = checkvalue(filenametoshow, dsc->control, "Maintainer"); if( RET_WAS_ERROR(r) ) return r; r = getvalue(filenametoshow, dsc->control, "Version", &dsc->version); if( RET_WAS_ERROR(r) ) return r; r = getvalue_n(dsc->control, SECTION_FIELDNAME, &dsc->section); if( RET_WAS_ERROR(r) ) return r; r = getvalue_n(dsc->control, PRIORITY_FIELDNAME, &dsc->priority); if( RET_WAS_ERROR(r) ) return r; for( cs = cs_md5sum ; cs < cs_hashCOUNT ; cs++ ) { assert( source_checksum_names[cs] != NULL ); r = chunk_getextralinelist(dsc->control, source_checksum_names[cs], &filelines[cs]); if( r == RET_NOTHING ) { if( cs == cs_md5sum ) { fprintf(stderr, "Missing 'Files' field in '%s'!\n", filenametoshow); r = RET_ERROR; } else strlist_init(&filelines[cs]); } if( RET_WAS_ERROR(r) ) { while( cs-- > cs_md5sum ) { strlist_done(&filelines[cs]); } return r; } } r = checksumsarray_parse(&dsc->files, filelines, filenametoshow); for( cs = cs_md5sum ; cs < cs_hashCOUNT ; cs++ ) { strlist_done(&filelines[cs]); } return r; } void sources_done(struct dsc_headers *dsc) { free(dsc->name); free(dsc->version); free(dsc->control); checksumsarray_done(&dsc->files); free(dsc->section); free(dsc->priority); } retvalue sources_complete(const struct dsc_headers *dsc, const char *directory, const struct overrideinfo *override, const char *section, const char *priority, char **newcontrol) { retvalue r; struct fieldtoadd *name; struct fieldtoadd *replace; char *newchunk,*newchunk2; char *newfilelines, *newsha1lines, *newsha256lines; assert(section != NULL && priority != NULL); /* first replace the "Source" with a "Package": */ name = addfield_new("Package",dsc->name,NULL); if( name == NULL ) return RET_ERROR_OOM; name = deletefield_new("Source",name); if( name == NULL ) return RET_ERROR_OOM; newchunk2 = chunk_replacefields(dsc->control, name, "Format", true); addfield_free(name); if( newchunk2 == NULL ) return RET_ERROR_OOM; r = checksumsarray_genfilelist(&dsc->files, &newfilelines, &newsha1lines, &newsha256lines); if( RET_WAS_ERROR(r) ) { free(newchunk2); return RET_ERROR_OOM; } assert( newfilelines != NULL ); replace = aodfield_new("Checksums-Sha256", newsha256lines, NULL); if( replace != NULL ) replace = aodfield_new("Checksums-Sha1", newsha1lines, replace); if( replace != NULL ) replace = addfield_new("Files", newfilelines, replace); if( replace != NULL ) replace = addfield_new("Directory",directory,replace); if( replace != NULL ) replace = deletefield_new("Status",replace); if( replace != NULL ) replace = addfield_new(SECTION_FIELDNAME,section,replace); if( replace != NULL ) replace = addfield_new(PRIORITY_FIELDNAME,priority,replace); if( replace != NULL ) replace = override_addreplacefields(override,replace); if( replace == NULL ) { free(newsha256lines); free(newsha1lines); free(newfilelines); free(newchunk2); return RET_ERROR_OOM; } newchunk = chunk_replacefields(newchunk2, replace, "Files", false); free(newsha256lines); free(newsha1lines); free(newfilelines); free(newchunk2); addfield_free(replace); if( newchunk == NULL ) { return RET_ERROR_OOM; } *newcontrol = newchunk; return RET_OK; } char *calc_source_basename(const char *name, const char *version) { const char *v = strchr(version, ':'); if( v != NULL ) v++; else v = version; return mprintf("%s_%s.dsc", name, v); } char *calc_sourcedir(component_t component, const char *sourcename) { assert( *sourcename != '\0' ); if( sourcename[0] == 'l' && sourcename[1] == 'i' && sourcename[2] == 'b' && sourcename[3] != '\0' ) return mprintf("pool/%s/lib%c/%s", atoms_components[component], sourcename[3], sourcename); else if( *sourcename != '\0' ) return mprintf("pool/%s/%c/%s", atoms_components[component], sourcename[0], sourcename); else return NULL; } char *calc_filekey(component_t component, const char *sourcename, const char *filename) { if( sourcename[0] == 'l' && sourcename[1] == 'i' && sourcename[2] == 'b' && sourcename[3] != '\0' ) return mprintf("pool/%s/lib%c/%s/%s", atoms_components[component], sourcename[3], sourcename, filename); else if( *sourcename != '\0' ) return mprintf("pool/%s/%c/%s/%s", atoms_components[component], sourcename[0], sourcename, filename); else return NULL; } char *calc_byhanddir(component_t component, const char *sourcename, const char *version) { if( sourcename[0] == 'l' && sourcename[1] == 'i' && sourcename[2] == 'b' && sourcename[3] != '\0' ) return mprintf("pool/%s/lib%c/%s/%s_%s_byhand", atoms_components[component], sourcename[3], sourcename, sourcename, version); else if( *sourcename != '\0' ) return mprintf("pool/%s/%c/%s/%s_%s_byhand", atoms_components[component], sourcename[0], sourcename, sourcename, version); else return NULL; }