server.c

Go to the documentation of this file.
00001 /*
00002  *  server.c
00003  *
00004  *  Copyright (c) 2006 by Miklos Vajna <vmiklos@frugalware.org>
00005  *
00006  *  This program is free software; you can redistribute it and/or modify
00007  *  it under the terms of the GNU General Public License as published by
00008  *  the Free Software Foundation; either version 2 of the License, or
00009  *  (at your option) any later version.
00010  *
00011  *  This program is distributed in the hope that it will be useful,
00012  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014  *  GNU General Public License for more details.
00015  *
00016  *  You should have received a copy of the GNU General Public License
00017  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
00018  */
00019 
00020 #include "config.h"
00021 
00022 #include <stdlib.h>
00023 #include <errno.h>
00024 #include <time.h>
00025 #include <string.h>
00026 #include <limits.h>
00027 #include <stdio.h>
00028 #include <sys/types.h>
00029 #include <sys/stat.h>
00030 #include <unistd.h>
00031 #include <download.h>
00032 
00033 /* libalpm */
00034 #include "server.h"
00035 #include "alpm_list.h"
00036 #include "error.h"
00037 #include "log.h"
00038 #include "alpm.h"
00039 #include "util.h"
00040 #include "handle.h"
00041 #include "package.h"
00042 
00043 pmserver_t *_alpm_server_new(const char *url)
00044 {
00045     struct url *u;
00046     pmserver_t *server;
00047 
00048     ALPM_LOG_FUNC;
00049 
00050     CALLOC(server, 1, sizeof(pmserver_t), RET_ERR(PM_ERR_MEMORY, NULL));
00051 
00052     u = downloadParseURL(url);
00053     if(!u) {
00054         _alpm_log(PM_LOG_ERROR, _("url '%s' is invalid, ignoring\n"), url);
00055         RET_ERR(PM_ERR_SERVER_BAD_URL, NULL);
00056     }
00057     if(strlen(u->scheme) == 0) {
00058         _alpm_log(PM_LOG_WARNING, _("url scheme not specified, assuming http\n"));
00059         strcpy(u->scheme, "http");
00060     }
00061 
00062     if(strcmp(u->scheme,"ftp") == 0 && strlen(u->user) == 0) {
00063         strcpy(u->user, "anonymous");
00064         strcpy(u->pwd, "libalpm@guest");
00065     }
00066 
00067     /* remove trailing slashes, just to clean up the rest of the code */
00068     for(int i = strlen(u->doc) - 1; u->doc[i] == '/'; --i)
00069         u->doc[i] = '\0';
00070 
00071   server->s_url = u;
00072 
00073     return server;
00074 }
00075 
00076 void _alpm_server_free(pmserver_t *server)
00077 {
00078     ALPM_LOG_FUNC;
00079 
00080     if(server == NULL) {
00081         return;
00082     }
00083 
00084     /* free memory */
00085     downloadFreeURL(server->s_url);
00086     FREE(server);
00087 }
00088 
00089 /* remove filename info from "s_url->doc" and return it */
00090 static char *strip_filename(pmserver_t *server)
00091 {
00092     char *p = NULL, *fname = NULL;
00093     if(!server) {
00094         return(NULL);
00095     }
00096 
00097     p = strrchr(server->s_url->doc, '/');
00098     if(p && *(++p)) {
00099         fname = strdup(p);
00100         _alpm_log(PM_LOG_DEBUG, "stripping '%s' from '%s'\n",
00101                 fname, server->s_url->doc);
00102         *p = 0;
00103     }
00104 
00105     /* s_url->doc now contains ONLY path information.  return value
00106      * if the file information from the original URL */
00107     return(fname);
00108 }
00109 
00110 /* Return a 'struct url' for this server, for downloading 'filename'. */
00111 static struct url *url_for_file(pmserver_t *server, const char *filename)
00112 {
00113     struct url *ret = NULL;
00114     char *doc = NULL;
00115     int doclen = 0;
00116 
00117     doclen = strlen(server->s_url->doc) + strlen(filename) + 2;
00118     CALLOC(doc, doclen, sizeof(char), RET_ERR(PM_ERR_MEMORY, NULL));
00119 
00120     snprintf(doc, doclen, "%s/%s", server->s_url->doc, filename);
00121     ret = downloadMakeURL(server->s_url->scheme,
00122                                                 server->s_url->host,
00123                                                 server->s_url->port,
00124                                                 doc,
00125                                                 server->s_url->user,
00126                                                 server->s_url->pwd);
00127     FREE(doc);
00128     return(ret);
00129 }
00130 
00131 /*
00132  * Download a list of files from a list of servers
00133  *   - if one server fails, we try the next one in the list
00134  *   - if *dl_total is non-NULL, then it will be used as the starting
00135  *     download amount when TotalDownload is set. It will also be
00136  *     set to the final download amount for the calling function to use.
00137  *   - totalsize is the total download size for use when TotalDownload
00138  *     is set. Use 0 if the total download size is not known.
00139  *
00140  * RETURN:  0 for successful download, 1 on error
00141  */
00142 int _alpm_downloadfiles(alpm_list_t *servers, const char *localpath,
00143         alpm_list_t *files, int *dl_total, unsigned long totalsize)
00144 {
00145     return(_alpm_downloadfiles_forreal(servers, localpath, files, 0, NULL,
00146                 dl_total, totalsize));
00147 }
00148 
00149 /*
00150  * This is the real downloadfiles, used directly by sync_synctree() to check
00151  * modtimes on remote files.
00152  *   - if mtime1 is non-NULL, then only download files if they are different
00153  *     than mtime1.
00154  *   - if *mtime2 is non-NULL, it will be filled with the mtime of the remote
00155  *     file.
00156  *   - if *dl_total is non-NULL, then it will be used as the starting
00157  *     download amount when TotalDownload is set. It will also be
00158  *     set to the final download amount for the calling function to use.
00159  *   - totalsize is the total download size for use when TotalDownload
00160  *     is set. Use 0 if the total download size is not known.
00161  *
00162  * RETURN:  0 for successful download
00163  *          1 if the mtimes are identical
00164  *         -1 on error
00165  */
00166 int _alpm_downloadfiles_forreal(alpm_list_t *servers, const char *localpath,
00167     alpm_list_t *files, time_t mtime1, time_t *mtime2, int *dl_total,
00168     unsigned long totalsize)
00169 {
00170     int dl_thisfile = 0;
00171     alpm_list_t *lp;
00172     int done = 0;
00173     alpm_list_t *complete = NULL;
00174     alpm_list_t *i;
00175 
00176     ALPM_LOG_FUNC;
00177 
00178     if(files == NULL) {
00179         return(0);
00180     }
00181 
00182     for(i = servers; i && !done; i = i->next) {
00183         pmserver_t *server = i->data;
00184 
00185         /* get each file in the list */
00186         for(lp = files; lp; lp = lp->next) {
00187             struct url *fileurl = NULL;
00188             char realfile[PATH_MAX];
00189             char output[PATH_MAX];
00190             char *fn = (char *)lp->data;
00191             char *pkgname;
00192 
00193             fileurl = url_for_file(server, fn);
00194             if(!fileurl) {
00195                 return(-1);
00196             }
00197 
00198             /* pass the raw filename for passing to the callback function */
00199             STRDUP(pkgname, fn, (void)0);
00200             _alpm_log(PM_LOG_DEBUG, "using '%s' for download progress\n", pkgname);
00201 
00202             snprintf(realfile, PATH_MAX, "%s%s", localpath, fn);
00203             snprintf(output, PATH_MAX, "%s%s.part", localpath, fn);
00204 
00205             if(alpm_list_find_str(complete, fn)) {
00206                 continue;
00207             }
00208 
00209             if(!handle->xfercommand || !strcmp(fileurl->scheme, "file")) {
00210                 FILE *dlf, *localf = NULL;
00211                 struct url_stat ust;
00212                 struct stat st;
00213                 int chk_resume = 0;
00214 
00215                 if(stat(output, &st) == 0 && st.st_size > 0) {
00216                     _alpm_log(PM_LOG_DEBUG, "existing file found, using it\n");
00217                     fileurl->offset = (off_t)st.st_size;
00218                     dl_thisfile = st.st_size;
00219                     if (dl_total != NULL) {
00220                         *dl_total += st.st_size;
00221                     }
00222                     localf = fopen(output, "a");
00223                     chk_resume = 1;
00224                 } else {
00225                     fileurl->offset = (off_t)0;
00226                     dl_thisfile = 0;
00227                 }
00228 
00229                 /* libdownload does not reset the error code, reset it in
00230                  * the case of previous errors */
00231                 downloadLastErrCode = 0;
00232 
00233                 /* 10s timeout - TODO make a config option */
00234                 downloadTimeout = 10000;
00235 
00236                 dlf = downloadXGet(fileurl, &ust, (handle->nopassiveftp ? "" : "p"));
00237 
00238                 if(downloadLastErrCode != 0 || dlf == NULL) {
00239                     const char *host = _("disk");
00240                     if(strcmp(SCHEME_FILE, fileurl->scheme) != 0) {
00241                         host = fileurl->host;
00242                     }
00243                     _alpm_log(PM_LOG_ERROR, _("failed retrieving file '%s' from %s : %s\n"),
00244                                         fn, host, downloadLastErrString);
00245                     if(localf != NULL) {
00246                         fclose(localf);
00247                     }
00248                     /* try the next server */
00249                     downloadFreeURL(fileurl);
00250                     continue;
00251                 } else {
00252                         _alpm_log(PM_LOG_DEBUG, "connected to %s successfully\n", fileurl->host);
00253                 }
00254 
00255                 if(ust.mtime && mtime1 && ust.mtime == mtime1) {
00256                     _alpm_log(PM_LOG_DEBUG, "mtimes are identical, skipping %s\n", fn);
00257                     complete = alpm_list_add(complete, fn);
00258                     if(localf != NULL) {
00259                         fclose(localf);
00260                     }
00261                     if(dlf != NULL) {
00262                         fclose(dlf);
00263                     }
00264                     downloadFreeURL(fileurl);
00265                     return(1);
00266                 }
00267 
00268                 if(ust.mtime && mtime2) {
00269                     *mtime2 = ust.mtime;
00270                 }
00271 
00272                 if(chk_resume && fileurl->offset == 0) {
00273                     _alpm_log(PM_LOG_WARNING, _("cannot resume download, starting over\n"));
00274                     if(localf != NULL) {
00275                         fclose(localf);
00276                         localf = NULL;
00277                     }
00278                 }
00279 
00280                 if(localf == NULL) {
00281                     _alpm_rmrf(output);
00282                     fileurl->offset = (off_t)0;
00283                     dl_thisfile = 0;
00284                     localf = fopen(output, "w");
00285                     if(localf == NULL) { /* still null? */
00286                         _alpm_log(PM_LOG_ERROR, _("cannot write to file '%s'\n"), output);
00287                         if(dlf != NULL) {
00288                             fclose(dlf);
00289                         }
00290                         downloadFreeURL(fileurl);
00291                         return(-1);
00292                     }
00293                 }
00294 
00295                 /* Progress 0 - initialize */
00296                 if(handle->dlcb) {
00297                     handle->dlcb(pkgname, 0, ust.size, dl_total ? *dl_total : 0,
00298                             totalsize);
00299                 }
00300 
00301                 int nread = 0;
00302                 char buffer[PM_DLBUF_LEN];
00303                 while((nread = fread(buffer, 1, PM_DLBUF_LEN, dlf)) > 0) {
00304                     if(ferror(dlf)) {
00305                         _alpm_log(PM_LOG_ERROR, _("error downloading '%s': %s\n"),
00306                                             fn, downloadLastErrString);
00307                         fclose(localf);
00308                         fclose(dlf);
00309                         downloadFreeURL(fileurl);
00310                         return(-1);
00311                     }
00312 
00313                     int nwritten = 0;
00314                     while(nwritten < nread) {
00315                         nwritten += fwrite(buffer, 1, (nread - nwritten), localf);
00316                         if(ferror(localf)) {
00317                             _alpm_log(PM_LOG_ERROR, _("error writing to file '%s': %s\n"),
00318                                                 realfile, strerror(errno));
00319                             fclose(localf);
00320                             fclose(dlf);
00321                             downloadFreeURL(fileurl);
00322                             return(-1);
00323                         }
00324                     }
00325 
00326                     if(nwritten != nread) {
00327 
00328                     }
00329                     dl_thisfile += nread;
00330                     if (dl_total != NULL) {
00331                         *dl_total += nread;
00332                     }
00333 
00334                     if(handle->dlcb) {
00335                         handle->dlcb(pkgname, dl_thisfile, ust.size,
00336                                 dl_total ? *dl_total : 0, totalsize);
00337                     }
00338                 }
00339 
00340                 downloadFreeURL(fileurl);
00341                 fclose(localf);
00342                 fclose(dlf);
00343                 rename(output, realfile);
00344                 complete = alpm_list_add(complete, fn);
00345             } else {
00346                 int ret;
00347                 int usepart = 0;
00348                 char *ptr1, *ptr2;
00349                 char origCmd[PATH_MAX];
00350                 char parsedCmd[PATH_MAX] = "";
00351                 char url[PATH_MAX];
00352                 char cwd[PATH_MAX];
00353 
00354                 /* build the full download url */
00355                 snprintf(url, PATH_MAX, "%s://%s%s", fileurl->scheme,
00356                         fileurl->host, fileurl->doc);
00357                 /* we don't need this anymore */
00358                 downloadFreeURL(fileurl);
00359 
00360                 /* replace all occurrences of %o with fn.part */
00361                 strncpy(origCmd, handle->xfercommand, sizeof(origCmd));
00362                 ptr1 = origCmd;
00363                 while((ptr2 = strstr(ptr1, "%o"))) {
00364                     usepart = 1;
00365                     ptr2[0] = '\0';
00366                     strcat(parsedCmd, ptr1);
00367                     strcat(parsedCmd, output);
00368                     ptr1 = ptr2 + 2;
00369                 }
00370                 strcat(parsedCmd, ptr1);
00371                 /* replace all occurrences of %u with the download URL */
00372                 strncpy(origCmd, parsedCmd, sizeof(origCmd));
00373                 parsedCmd[0] = '\0';
00374                 ptr1 = origCmd;
00375                 while((ptr2 = strstr(ptr1, "%u"))) {
00376                     ptr2[0] = '\0';
00377                     strcat(parsedCmd, ptr1);
00378                     strcat(parsedCmd, url);
00379                     ptr1 = ptr2 + 2;
00380                 }
00381                 strcat(parsedCmd, ptr1);
00382                 /* cwd to the download directory */
00383                 getcwd(cwd, PATH_MAX);
00384                 if(chdir(localpath)) {
00385                     _alpm_log(PM_LOG_WARNING, _("could not chdir to %s\n"), localpath);
00386                     return(PM_ERR_CONNECT_FAILED);
00387                 }
00388                 /* execute the parsed command via /bin/sh -c */
00389                 _alpm_log(PM_LOG_DEBUG, "running command: %s\n", parsedCmd);
00390                 ret = system(parsedCmd);
00391                 if(ret == -1) {
00392                     _alpm_log(PM_LOG_WARNING, _("running XferCommand: fork failed!\n"));
00393                     return(PM_ERR_FORK_FAILED);
00394                 } else if(ret != 0) {
00395                     /* download failed */
00396                     _alpm_log(PM_LOG_DEBUG, "XferCommand command returned non-zero status code (%d)\n", ret);
00397                 } else {
00398                     /* download was successful */
00399                     complete = alpm_list_add(complete, fn);
00400                     if(usepart) {
00401                         rename(output, realfile);
00402                     }
00403                 }
00404                 chdir(cwd);
00405             }
00406             FREE(pkgname);
00407         }
00408 
00409         if(alpm_list_count(complete) == alpm_list_count(files)) {
00410             done = 1;
00411         }
00412     }
00413     alpm_list_free(complete);
00414 
00415     return(done ? 0 : -1);
00416 }
00417 
00418 /** Fetch a remote pkg.
00419  * @param url URL of the package to download
00420  * @return the downloaded filepath on success, NULL on error
00421  * @addtogroup alpm_misc
00422  */
00423 char SYMEXPORT *alpm_fetch_pkgurl(const char *url)
00424 {
00425     pmserver_t *server;
00426     char *filename, *filepath;
00427     const char *cachedir;
00428 
00429     ALPM_LOG_FUNC;
00430 
00431     if(strstr(url, "://") == NULL) {
00432         _alpm_log(PM_LOG_DEBUG, "Invalid URL passed to alpm_fetch_pkgurl\n");
00433         return(NULL);
00434     }
00435 
00436     server = _alpm_server_new(url);
00437     if(!server) {
00438         return(NULL);
00439     }
00440 
00441     /* strip path information from the filename */
00442     filename = strip_filename(server);
00443     if(!filename) {
00444         _alpm_log(PM_LOG_ERROR, _("URL does not contain a file for download\n"));
00445         return(NULL);
00446     }
00447 
00448     /* find a valid cache dir to download to */
00449     cachedir = _alpm_filecache_setup();
00450 
00451     /* TODO this seems like needless complexity just to download one file */
00452     alpm_list_t *servers = alpm_list_add(NULL, server);
00453     alpm_list_t *files = alpm_list_add(NULL, filename);
00454 
00455     /* download the file */
00456     if(_alpm_downloadfiles(servers, cachedir, files, NULL, 0)) {
00457         _alpm_log(PM_LOG_WARNING, _("failed to download %s\n"), url);
00458         return(NULL);
00459     }
00460     _alpm_log(PM_LOG_DEBUG, "successfully downloaded %s\n", filename);
00461     alpm_list_free(files);
00462     alpm_list_free(servers);
00463     _alpm_server_free(server);
00464 
00465     /* we should be able to find the file the second time around */
00466     filepath = _alpm_filecache_find(filename);
00467     return(filepath);
00468 }
00469 
00470 /* vim: set ts=2 sw=2 noet: */

Generated on Mon Jan 14 23:53:40 2008 for libalpm by  doxygen 1.5.4