source: trunk/base/src/pextlib1.0/curl.c @ 51282

Last change on this file since 51282 was 51282, checked in by toby@…, 8 years ago

easier check for curl_easy_strerror
misc cleanup

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 23.9 KB
Line 
1/*
2 * curl.c
3 * $Id: curl.c 51282 2009-05-22 06:52:05Z toby@macports.org $
4 *
5 * Copyright (c) 2005 Paul Guyot, The MacPorts Project.
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 * 3. Neither the name of MacPorts Team nor the names of its contributors
17 *    may be used to endorse or promote products derived from this software
18 *    without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
24 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30 * POSSIBILITY OF SUCH DAMAGE.
31 */
32
33#if HAVE_CONFIG_H
34#include <config.h>
35#endif
36
37#include <ctype.h>
38#include <errno.h>
39#include <stdio.h>
40#include <stdlib.h>
41#include <string.h>
42
43#ifdef HAVE_UTIME_H
44#include <utime.h>
45#endif
46
47#include <curl/curl.h>
48
49#include <tcl.h>
50
51#include "curl.h"
52
53/*
54 * Some compiled-in constants that we may wish to change later, given more
55 * empirical data.  These represent "best guess" values for now.
56 */
57#define _CURL_CONNECTION_TIMEOUT        ((long)(5 * 60))        /* 5 minutes */
58#define _CURL_MINIMUM_XFER_SPEED        ((long)1024)            /* 1Kb/sec */
59#define _CURL_MINIMUM_XFER_TIMEOUT      ((long)(10 * 60))       /* 10 minutes */
60
61/* ========================================================================= **
62 * Definitions
63 * ========================================================================= */
64#pragma mark Definitions
65
66/* ------------------------------------------------------------------------- **
67 * Prototypes
68 * ------------------------------------------------------------------------- */
69int SetResultFromCurlErrorCode(Tcl_Interp* interp, CURLcode inErrorCode);
70int CurlFetchCmd(Tcl_Interp* interp, int objc, Tcl_Obj* CONST objv[]);
71int CurlIsNewerCmd(Tcl_Interp* interp, int objc, Tcl_Obj* CONST objv[]);
72int CurlGetSizeCmd(Tcl_Interp* interp, int objc, Tcl_Obj* CONST objv[]);
73
74/* ========================================================================= **
75 * Entry points
76 * ========================================================================= */
77#pragma mark -
78#pragma mark Entry points
79
80/**
81 * Set the result if a libcurl error occurred return TCL_ERROR.
82 * Otherwise, set the result to "" and return TCL_OK.
83 *
84 * @param interp                pointer to the interpreter.
85 * @param inErrorCode   code of the error.
86 * @return TCL_OK if inErrorCode is 0, TCL_ERROR otherwise.
87 */
88int
89SetResultFromCurlErrorCode(Tcl_Interp* interp, CURLcode inErrorCode)
90{
91        int theResult;
92
93        switch(inErrorCode)
94        {
95                case CURLE_OK:
96                        Tcl_SetResult(interp, "", TCL_STATIC);
97                        theResult = TCL_OK;
98                        break;
99               
100                default: {
101#if LIBCURL_VERSION_NUM >= 0x070c00 /* 7.12.0 */
102                        Tcl_SetResult(interp, (char *)curl_easy_strerror(inErrorCode), TCL_VOLATILE);
103#else
104                        char theErrorString[512];
105                        (void)snprintf(theErrorString, sizeof(theErrorString), "curl error %i", inErrorCode);
106                        Tcl_SetResult(interp, theErrorString, TCL_VOLATILE);
107#endif
108                        theResult = TCL_ERROR;
109                }
110        }
111       
112        return theResult;
113}
114
115/**
116 * curl fetch subcommand entry point.
117 *
118 * syntax: curl fetch [-v] [--disable-epsv] [--ignore-ssl-cert] [--remote-time] [-u userpass] [--effective-url lasturlvar] url filename
119 *
120 * @param interp                current interpreter
121 * @param objc                  number of parameters
122 * @param objv                  parameters
123 */
124int
125CurlFetchCmd(Tcl_Interp* interp, int objc, Tcl_Obj* CONST objv[])
126{
127        int theResult = TCL_OK;
128        CURL* theHandle = NULL;
129        FILE* theFile = NULL;
130
131        do {
132                long theResponseCode = 0;
133                int noprogress = 1;
134                int useepsv = 1;
135                int ignoresslcert = 0;
136                int remotetime = 0;
137                const char* theUserPassString = NULL;
138                const char* effectiveURLVarName = NULL;
139                char* effectiveURL = NULL;
140                char* userAgent = PACKAGE_STRING " libcurl/" LIBCURL_VERSION;
141                int optioncrsr;
142                int lastoption;
143                const char* theURL;
144                const char* theFilePath;
145                long theFileTime = 0;
146                CURLcode theCurlCode;
147                struct curl_slist *headers = NULL;
148               
149                /* we might have options and then the url and the file */
150                /* let's process the options first */
151               
152                optioncrsr = 2;
153                lastoption = objc - 3;
154                while (optioncrsr <= lastoption) {
155                        /* get the option */
156                        const char* theOption = Tcl_GetString(objv[optioncrsr]);
157                       
158                        if (strcmp(theOption, "-v") == 0) {
159                                noprogress = 0;
160                        } else if (strcmp(theOption, "--disable-epsv") == 0) {
161                                useepsv = 0;
162                        } else if (strcmp(theOption, "--ignore-ssl-cert") == 0) {
163                                ignoresslcert = 1;
164                        } else if (strcmp(theOption, "--remote-time") == 0) {
165                                remotetime = 1;
166                        } else if (strcmp(theOption, "-u") == 0) {
167                                /* check we also have the parameter */
168                                if (optioncrsr < lastoption) {
169                                        optioncrsr++;
170                                        theUserPassString = Tcl_GetString(objv[optioncrsr]);
171                                } else {
172                                        Tcl_SetResult(interp,
173                                                "curl fetch: -u option requires a parameter",
174                                                TCL_STATIC);
175                                        theResult = TCL_ERROR;
176                                        break;                                 
177                                }
178                        } else if (strcmp(theOption, "--effective-url") == 0) {
179                                /* check we also have the parameter */
180                                if (optioncrsr < lastoption) {
181                                        optioncrsr++;
182                                        effectiveURLVarName = Tcl_GetString(objv[optioncrsr]);
183                                } else {
184                                        Tcl_SetResult(interp,
185                                                "curl fetch: --effective-url option requires a parameter",
186                                                TCL_STATIC);
187                                        theResult = TCL_ERROR;
188                                        break;                                 
189                                }
190                        } else if (strcmp(theOption, "--user-agent") == 0) {
191                                /* check we also have the parameter */
192                                if (optioncrsr < lastoption) {
193                                        optioncrsr++;
194                                        userAgent = Tcl_GetString(objv[optioncrsr]);
195                                } else {
196                                        Tcl_SetResult(interp,
197                                                "curl fetch: --user-agent option requires a parameter",
198                                                TCL_STATIC);
199                                        theResult = TCL_ERROR;
200                                        break;                                 
201                                }
202                        } else {
203                                char theErrorString[512];
204                                (void) snprintf(theErrorString, sizeof(theErrorString),
205                                        "curl fetch: unknown option %s", theOption);
206                                Tcl_SetResult(interp, theErrorString, TCL_VOLATILE);
207                                theResult = TCL_ERROR;
208                                break;
209                        }
210                       
211                        optioncrsr++;
212                }
213               
214                if (optioncrsr <= lastoption) {
215                        /* something went wrong */
216                        break;
217                }
218
219                /*      first (second) parameter is -v or the url,
220                        second (third) parameter is the file */
221
222                if (objc >= 4) {
223                        /* Retrieve the url */
224                        theURL = Tcl_GetString(objv[objc - 2]);
225       
226                        /* Retrieve the file path */
227                        theFilePath = Tcl_GetString(objv[objc - 1]);
228                } else {
229                        Tcl_WrongNumArgs(interp, 1, objv, "fetch [options] url file");
230                        theResult = TCL_ERROR;
231                        break;
232                }
233               
234                /* Open the file */
235                theFile = fopen( theFilePath, "w" );
236                if (theFile == NULL) {
237                        Tcl_SetResult(interp, strerror(errno), TCL_VOLATILE);
238                        theResult = TCL_ERROR;
239                        break;
240                }
241
242                /* Create the CURL handle */
243                theHandle = curl_easy_init();
244               
245                /* Setup the handle */
246                theCurlCode = curl_easy_setopt(theHandle, CURLOPT_URL, theURL);
247                if (theCurlCode != CURLE_OK) {
248                        theResult = SetResultFromCurlErrorCode(interp, theCurlCode);
249                        break;
250                }
251               
252                /* -L option */
253                theCurlCode = curl_easy_setopt(theHandle, CURLOPT_FOLLOWLOCATION, 1);
254                if (theCurlCode != CURLE_OK) {
255                        theResult = SetResultFromCurlErrorCode(interp, theCurlCode);
256                        break;
257                }
258
259                /* --max-redirs option, same default as curl command line */
260                theCurlCode = curl_easy_setopt(theHandle, CURLOPT_MAXREDIRS, 50);
261                if (theCurlCode != CURLE_OK) {
262                        theResult = SetResultFromCurlErrorCode(interp, theCurlCode);
263                        break;
264                }
265
266                /* -f option */
267                theCurlCode = curl_easy_setopt(theHandle, CURLOPT_FAILONERROR, 1);
268                if (theCurlCode != CURLE_OK) {
269                        theResult = SetResultFromCurlErrorCode(interp, theCurlCode);
270                        break;
271                }
272
273        /* -A option */
274        theCurlCode = curl_easy_setopt(theHandle, CURLOPT_USERAGENT, userAgent);
275        if (theCurlCode != CURLE_OK) {
276            theResult = SetResultFromCurlErrorCode(interp, theCurlCode);
277            break;
278        }
279
280                /* skip the header data */
281                theCurlCode = curl_easy_setopt(theHandle, CURLOPT_HEADER, 0);
282                if (theCurlCode != CURLE_OK) {
283                        theResult = SetResultFromCurlErrorCode(interp, theCurlCode);
284                        break;
285                }
286               
287                /* write to the file */
288                theCurlCode = curl_easy_setopt(theHandle, CURLOPT_WRITEDATA, theFile);
289                if (theCurlCode != CURLE_OK) {
290                        theResult = SetResultFromCurlErrorCode(interp, theCurlCode);
291                        break;
292                }
293               
294                /* we want/don't want progress */
295                theCurlCode = curl_easy_setopt(theHandle, CURLOPT_NOPROGRESS, noprogress);
296                if (theCurlCode != CURLE_OK) {
297                        theResult = SetResultFromCurlErrorCode(interp, theCurlCode);
298                        break;
299                }
300
301                /* we want/don't want to use epsv */
302                theCurlCode = curl_easy_setopt(theHandle, CURLOPT_FTP_USE_EPSV, useepsv);
303                if (theCurlCode != CURLE_OK) {
304                        theResult = SetResultFromCurlErrorCode(interp, theCurlCode);
305                        break;
306                }
307               
308                /* we may want to ignore ssl errors */
309                if (ignoresslcert) {
310                        theCurlCode = curl_easy_setopt(theHandle, CURLOPT_SSL_VERIFYPEER, (long) 0);
311                        if (theCurlCode != CURLE_OK) {
312                                theResult = SetResultFromCurlErrorCode(interp, theCurlCode);
313                                break;
314                        }
315                        theCurlCode = curl_easy_setopt(theHandle, CURLOPT_SSL_VERIFYHOST, (long) 0);
316                        if (theCurlCode != CURLE_OK) {
317                                theResult = SetResultFromCurlErrorCode(interp, theCurlCode);
318                                break;
319                        }
320                }
321
322                /* we want/don't want remote time */
323                theCurlCode = curl_easy_setopt(theHandle, CURLOPT_FILETIME, remotetime);
324                if (theCurlCode != CURLE_OK) {
325                        theResult = SetResultFromCurlErrorCode(interp, theCurlCode);
326                        break;
327                }
328
329                /* set the l/p, if any */
330                if (theUserPassString) {
331                        theCurlCode = curl_easy_setopt(theHandle, CURLOPT_USERPWD, theUserPassString);
332                        if (theCurlCode != CURLE_OK) {
333                                theResult = SetResultFromCurlErrorCode(interp, theCurlCode);
334                                break;
335                        }
336                }
337
338                /* Clear the Pragma: no-cache header */
339                headers = curl_slist_append(headers, "Pragma:");
340                theCurlCode = curl_easy_setopt(theHandle, CURLOPT_HTTPHEADER, headers);
341                if (theCurlCode != CURLE_OK) {
342                        theResult = SetResultFromCurlErrorCode(interp, theCurlCode);
343                        break;
344                }
345
346                /* actually fetch the resource */
347                theCurlCode = curl_easy_perform(theHandle);
348                if (theCurlCode != CURLE_OK) {
349                        theResult = SetResultFromCurlErrorCode(interp, theCurlCode);
350                        break;
351                }
352               
353                /* close the file */
354                (void) fclose( theFile );
355                theFile = NULL;
356               
357#if LIBCURL_VERSION_NUM == 0x070d01 /* work around broken Tiger version of cURL */
358                if (remotetime) {
359                        FILE *fp;
360                        char *tmp, *p;
361                        char buf[BUFSIZ];
362                        size_t size;
363                       
364                        tmp = tmpnam(NULL);
365                        fp = fopen(tmp, "w");
366                        if (fp == NULL) {
367                                Tcl_SetResult(interp, strerror(errno), TCL_VOLATILE);
368                                theResult = TCL_ERROR;
369                                break;
370                        }
371                        theFile = fopen( theFilePath, "r");
372                        if (theFile == NULL) {
373                                Tcl_SetResult(interp, strerror(errno), TCL_VOLATILE);
374                                theResult = TCL_ERROR;
375                                break;
376                        }
377                        if ( (p = fgets(buf, BUFSIZ, theFile)) != NULL) {
378                                /* skip stray header escaping into output */
379                                if (strncmp(p, "Last-Modified:", 14) != 0)
380                                        rewind(theFile);
381                        }
382                        while ( (size = fread(buf, 1, BUFSIZ, theFile)) > 0) {
383                                fwrite(buf, 1, size, fp);
384                        }
385                        (void) fclose( theFile );
386                        theFile = NULL;
387                        fclose(fp);
388                        if (rename(tmp, theFilePath) != 0) {
389                                Tcl_SetResult(interp, strerror(errno), TCL_VOLATILE);
390                                theResult = TCL_ERROR;
391                                break;
392                        }
393                }
394#endif
395
396#ifdef HAVE_UTIME_H
397                if (remotetime) {
398                        theCurlCode = curl_easy_getinfo(theHandle, CURLINFO_FILETIME, &theFileTime);
399                        if (theFileTime > 0) {
400                                struct utimbuf times;
401                                times.actime = (time_t)theFileTime;
402                                times.modtime = (time_t)theFileTime;
403                                utime(theFilePath, &times); /* set the time we got */
404                        }
405                }
406#endif /*HAVE_UTIME_H*/
407               
408                /* free header memory */
409                curl_slist_free_all(headers);
410
411                /* If --effective-url option was given, set given variable name to last effective url used by curl */
412                if (effectiveURLVarName != NULL) {
413                        theCurlCode = curl_easy_getinfo(theHandle, CURLINFO_EFFECTIVE_URL, &effectiveURL);
414                        Tcl_SetVar(interp, effectiveURLVarName,
415                                (effectiveURL == NULL || theCurlCode != CURLE_OK) ? "" : effectiveURL,
416                                0);
417                }
418               
419                /* check everything went fine */
420                theCurlCode = curl_easy_getinfo(theHandle, CURLINFO_HTTP_CODE, &theResponseCode);
421                if (theCurlCode != CURLE_OK) {
422                        theResult = SetResultFromCurlErrorCode(interp, theCurlCode);
423                        break;
424                }
425               
426                /* we need something between 200 (incl.) and 300 (excl.).*/
427                /* (actually, we sometimes get 0 from GNU FTP servers) */
428                if (((theResponseCode != 0)  && (theResponseCode < 200))
429                        || (theResponseCode >= 300)) {
430                        char theErrorString[512];
431                        (void) snprintf(theErrorString, sizeof(theErrorString),
432                                "Download failed (code = %li)", theResponseCode);
433                        Tcl_SetResult(interp, theErrorString, TCL_VOLATILE);
434                        theResult = TCL_ERROR;
435                        break;
436                }
437               
438                /* clean up */
439                curl_easy_cleanup( theHandle );
440                theHandle = NULL;
441    } while (0);
442   
443    if (theHandle != NULL) {
444        curl_easy_cleanup( theHandle );
445    }
446    if (theFile != NULL) {
447        fclose( theFile );
448    }
449   
450        return theResult;
451}
452
453/**
454 * curl isnewer subcommand entry point.
455 *
456 * @param interp                current interpreter
457 * @param objc                  number of parameters
458 * @param objv                  parameters
459 */
460int
461CurlIsNewerCmd(Tcl_Interp* interp, int objc, Tcl_Obj* CONST objv[])
462{
463        int theResult = TCL_OK;
464        CURL* theHandle = NULL;
465        FILE* theFile = NULL;
466
467        do {
468                long theResponseCode = 0;
469                const char* theURL;
470                CURLcode theCurlCode;
471                long theModDate;
472                long userModDate;
473                               
474                /*      first (second) parameter is the url,
475                        second (third) parameter is the date */
476                if (objc != 4) {
477                        Tcl_WrongNumArgs(interp, 1, objv, "isnewer url date");
478                        theResult = TCL_ERROR;
479                        break;
480                }
481
482                /* Retrieve the url */
483                theURL = Tcl_GetString(objv[2]);
484
485                /* Get the date */
486                theResult = Tcl_GetLongFromObj(interp, objv[3], &userModDate);
487                if (theResult != TCL_OK) {
488                        break;
489                }
490               
491                /* Open the file (dev/null) */
492                theFile = fopen( "/dev/null", "a" );
493                if (theFile == NULL) {
494                        Tcl_SetResult(interp, strerror(errno), TCL_VOLATILE);
495                        theResult = TCL_ERROR;
496                        break;
497                }
498
499                /* Create the CURL handle */
500                theHandle = curl_easy_init();
501               
502                /* Setup the handle */
503                theCurlCode = curl_easy_setopt(theHandle, CURLOPT_URL, theURL);
504                if (theCurlCode != CURLE_OK) {
505                        theResult = SetResultFromCurlErrorCode(interp, theCurlCode);
506                        break;
507                }
508               
509                /* -L option */
510                theCurlCode = curl_easy_setopt(theHandle, CURLOPT_FOLLOWLOCATION, 1);
511                if (theCurlCode != CURLE_OK) {
512                        theResult = SetResultFromCurlErrorCode(interp, theCurlCode);
513                        break;
514                }
515
516                /* --max-redirs option, same default as curl command line */
517                theCurlCode = curl_easy_setopt(theHandle, CURLOPT_MAXREDIRS, 50);
518                if (theCurlCode != CURLE_OK) {
519                        theResult = SetResultFromCurlErrorCode(interp, theCurlCode);
520                        break;
521                }
522
523                /* -f option */
524                theCurlCode = curl_easy_setopt(theHandle, CURLOPT_FAILONERROR, 1);
525                if (theCurlCode != CURLE_OK) {
526                        theResult = SetResultFromCurlErrorCode(interp, theCurlCode);
527                        break;
528                }
529
530                /* set timeout on connections */
531                theCurlCode = curl_easy_setopt(theHandle, CURLOPT_TIMEOUT, _CURL_CONNECTION_TIMEOUT);
532                if (theCurlCode != CURLE_OK) {
533                        theResult = SetResultFromCurlErrorCode(interp, theCurlCode);
534                        break;
535                }
536
537                /* set minimum connection speed */
538                theCurlCode = curl_easy_setopt(theHandle, CURLOPT_LOW_SPEED_LIMIT, _CURL_MINIMUM_XFER_SPEED);
539                if (theCurlCode != CURLE_OK) {
540                        theResult = SetResultFromCurlErrorCode(interp, theCurlCode);
541                        break;
542                }
543
544                /* set timeout interval for connections < min xfer speed */
545                theCurlCode = curl_easy_setopt(theHandle, CURLOPT_LOW_SPEED_TIME, _CURL_MINIMUM_XFER_TIMEOUT);
546                if (theCurlCode != CURLE_OK) {
547                        theResult = SetResultFromCurlErrorCode(interp, theCurlCode);
548                        break;
549                }
550
551                /* write to the file */
552                theCurlCode = curl_easy_setopt(theHandle, CURLOPT_WRITEDATA, theFile);
553                if (theCurlCode != CURLE_OK) {
554                        theResult = SetResultFromCurlErrorCode(interp, theCurlCode);
555                        break;
556                }
557               
558                /* save the modification date */
559                theCurlCode = curl_easy_setopt(theHandle, CURLOPT_FILETIME, 1);
560                if (theCurlCode != CURLE_OK) {
561                        theResult = SetResultFromCurlErrorCode(interp, theCurlCode);
562                        break;
563                }
564
565                /* skip the download if the file wasn't modified */
566                theCurlCode = curl_easy_setopt(theHandle, CURLOPT_TIMECONDITION, CURL_TIMECOND_IFMODSINCE);
567                if (theCurlCode != CURLE_OK) {
568                        theResult = SetResultFromCurlErrorCode(interp, theCurlCode);
569                        break;
570                }
571                theCurlCode = curl_easy_setopt(theHandle, CURLOPT_TIMEVALUE, userModDate);
572                if (theCurlCode != CURLE_OK) {
573                        theResult = SetResultFromCurlErrorCode(interp, theCurlCode);
574                        break;
575                }
576
577                /* we do not want any progress */
578                theCurlCode = curl_easy_setopt(theHandle, CURLOPT_NOPROGRESS, 1);
579                if (theCurlCode != CURLE_OK) {
580                        theResult = SetResultFromCurlErrorCode(interp, theCurlCode);
581                        break;
582                }
583               
584                /* actually fetch the resource */
585                theCurlCode = curl_easy_perform(theHandle);
586                if (theCurlCode != CURLE_OK) {
587                        theResult = SetResultFromCurlErrorCode(interp, theCurlCode);
588                        break;
589                }
590               
591                /* close the file */
592                (void) fclose( theFile );
593                theFile = NULL;
594               
595                /* check everything went fine */
596                theCurlCode = curl_easy_getinfo(theHandle, CURLINFO_HTTP_CODE, &theResponseCode);
597                if (theCurlCode != CURLE_OK) {
598                        theResult = SetResultFromCurlErrorCode(interp, theCurlCode);
599                        break;
600                }
601
602                theModDate = -1;
603
604                if (theResponseCode != 304) {
605                        /* get the modification date */
606                        theCurlCode = curl_easy_getinfo(theHandle, CURLINFO_FILETIME, &theModDate);
607                        if (theCurlCode != CURLE_OK) {
608                                theResult = SetResultFromCurlErrorCode(interp, theCurlCode);
609                                break;
610                        }
611       
612                        /* clean up */
613                        curl_easy_cleanup( theHandle );
614                        theHandle = NULL;
615       
616                        /* compare this with the date provided by user */
617                        if (theModDate < -1) {
618                                Tcl_SetResult(interp, "Couldn't get resource modification date", TCL_STATIC);
619                                theResult = TCL_ERROR;
620                                break;
621                        }
622                }
623
624                if (theModDate > userModDate) {
625                        Tcl_SetResult(interp, "1", TCL_STATIC);
626                } else {
627                        Tcl_SetResult(interp, "0", TCL_STATIC);
628                }               
629    } while (0);
630   
631    if (theHandle != NULL) {
632        curl_easy_cleanup( theHandle );
633    }
634    if (theFile != NULL) {
635        fclose( theFile );
636    }
637   
638        return theResult;
639}
640
641/**
642 * curl getsize subcommand entry point.
643 *
644 * @param interp                current interpreter
645 * @param objc                  number of parameters
646 * @param objv                  parameters
647 */
648int
649CurlGetSizeCmd(Tcl_Interp* interp, int objc, Tcl_Obj* CONST objv[])
650{
651        int theResult = TCL_OK;
652        CURL* theHandle = NULL;
653        FILE* theFile = NULL;
654
655        do {
656                char theSizeString[32];
657                const char* theURL;
658                CURLcode theCurlCode;
659                double theFileSize;
660                               
661                /*      first (second) parameter is the url */
662                if (objc != 3) {
663                        Tcl_WrongNumArgs(interp, 1, objv, "getsize url");
664                        theResult = TCL_ERROR;
665                        break;
666                }
667
668                /* Retrieve the url */
669                theURL = Tcl_GetString(objv[2]);
670
671                /* Open the file (dev/null) */
672                theFile = fopen( "/dev/null", "a" );
673                if (theFile == NULL) {
674                        Tcl_SetResult(interp, strerror(errno), TCL_VOLATILE);
675                        theResult = TCL_ERROR;
676                        break;
677                }
678
679                /* Create the CURL handle */
680                theHandle = curl_easy_init();
681               
682                /* Setup the handle */
683                theCurlCode = curl_easy_setopt(theHandle, CURLOPT_URL, theURL);
684                if (theCurlCode != CURLE_OK) {
685                        theResult = SetResultFromCurlErrorCode(interp, theCurlCode);
686                        break;
687                }
688               
689                /* -L option */
690                theCurlCode = curl_easy_setopt(theHandle, CURLOPT_FOLLOWLOCATION, 1);
691                if (theCurlCode != CURLE_OK) {
692                        theResult = SetResultFromCurlErrorCode(interp, theCurlCode);
693                        break;
694                }
695
696                /* --max-redirs option, same default as curl command line */
697                theCurlCode = curl_easy_setopt(theHandle, CURLOPT_MAXREDIRS, 50);
698                if (theCurlCode != CURLE_OK) {
699                        theResult = SetResultFromCurlErrorCode(interp, theCurlCode);
700                        break;
701                }
702
703                /* -f option */
704                theCurlCode = curl_easy_setopt(theHandle, CURLOPT_FAILONERROR, 1);
705                if (theCurlCode != CURLE_OK) {
706                        theResult = SetResultFromCurlErrorCode(interp, theCurlCode);
707                        break;
708                }
709
710                /* set timeout on connections */
711                theCurlCode = curl_easy_setopt(theHandle, CURLOPT_TIMEOUT, _CURL_CONNECTION_TIMEOUT);
712                if (theCurlCode != CURLE_OK) {
713                        theResult = SetResultFromCurlErrorCode(interp, theCurlCode);
714                        break;
715                }
716
717                /* set minimum connection speed */
718                theCurlCode = curl_easy_setopt(theHandle, CURLOPT_LOW_SPEED_LIMIT, _CURL_MINIMUM_XFER_SPEED);
719                if (theCurlCode != CURLE_OK) {
720                        theResult = SetResultFromCurlErrorCode(interp, theCurlCode);
721                        break;
722                }
723
724                /* set timeout interval for connections < min xfer speed */
725                theCurlCode = curl_easy_setopt(theHandle, CURLOPT_LOW_SPEED_TIME, _CURL_MINIMUM_XFER_TIMEOUT);
726                if (theCurlCode != CURLE_OK) {
727                        theResult = SetResultFromCurlErrorCode(interp, theCurlCode);
728                        break;
729                }
730
731                /* write to the file */
732                theCurlCode = curl_easy_setopt(theHandle, CURLOPT_WRITEDATA, theFile);
733                if (theCurlCode != CURLE_OK) {
734                        theResult = SetResultFromCurlErrorCode(interp, theCurlCode);
735                        break;
736                }
737
738                /* skip the header data */
739                theCurlCode = curl_easy_setopt(theHandle, CURLOPT_HEADER, 0);
740                if (theCurlCode != CURLE_OK) {
741                        theResult = SetResultFromCurlErrorCode(interp, theCurlCode);
742                        break;
743                }
744               
745                /* skip the body data */
746                theCurlCode = curl_easy_setopt(theHandle, CURLOPT_NOBODY, 1);
747                if (theCurlCode != CURLE_OK) {
748                        theResult = SetResultFromCurlErrorCode(interp, theCurlCode);
749                        break;
750                }
751
752                /* we do not want any progress */
753                theCurlCode = curl_easy_setopt(theHandle, CURLOPT_NOPROGRESS, 1);
754                if (theCurlCode != CURLE_OK) {
755                        theResult = SetResultFromCurlErrorCode(interp, theCurlCode);
756                        break;
757                }
758               
759                /* actually fetch the resource */
760                theCurlCode = curl_easy_perform(theHandle);
761                if (theCurlCode != CURLE_OK) {
762                        theResult = SetResultFromCurlErrorCode(interp, theCurlCode);
763                        break;
764                }
765               
766                /* close the file */
767                (void) fclose( theFile );
768                theFile = NULL;
769
770                theFileSize = 0.0;
771
772                /* get the file size */
773                theCurlCode = curl_easy_getinfo(theHandle, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &theFileSize);
774                if (theCurlCode != CURLE_OK) {
775                        theResult = SetResultFromCurlErrorCode(interp, theCurlCode);
776                        break;
777                }
778       
779                /* clean up */
780                curl_easy_cleanup( theHandle );
781                theHandle = NULL;
782
783                (void) snprintf(theSizeString, sizeof(theSizeString),
784                        "%.0f", theFileSize);
785                Tcl_SetResult(interp, theSizeString, TCL_VOLATILE);
786    } while (0);
787   
788    if (theHandle != NULL) {
789        curl_easy_cleanup( theHandle );
790    }
791    if (theFile != NULL) {
792        fclose( theFile );
793    }
794   
795        return theResult;
796}
797
798/**
799 * curl command entry point.
800 *
801 * @param clientData    custom data (ignored)
802 * @param interp                current interpreter
803 * @param objc                  number of parameters
804 * @param objv                  parameters
805 */
806int
807CurlCmd(
808                ClientData clientData UNUSED,
809                Tcl_Interp* interp,
810                int objc, 
811                Tcl_Obj* CONST objv[])
812{
813    typedef enum {
814        kCurlFetch,
815        kCurlIsNewer,
816        kCurlGetSize
817    } EOption;
818   
819        static const char *options[] = {
820                "fetch", "isnewer", "getsize", NULL
821        };
822        int theResult = TCL_OK;
823    EOption theOptionIndex;
824
825        if (objc < 3) {
826                Tcl_WrongNumArgs(interp, 1, objv, "option ?arg ...?");
827                return TCL_ERROR;
828        }
829
830        theResult = Tcl_GetIndexFromObj(
831                                interp,
832                                objv[1],
833                                options,
834                                "option",
835                                0,
836                                (int*) &theOptionIndex);
837        if (theResult == TCL_OK) {
838                switch (theOptionIndex)
839                {
840                        case kCurlFetch:
841                                theResult = CurlFetchCmd(interp, objc, objv);
842                                break;
843
844                        case kCurlIsNewer:
845                                theResult = CurlIsNewerCmd(interp, objc, objv);
846                                break;
847
848                        case kCurlGetSize:
849                                theResult = CurlGetSizeCmd(interp, objc, objv);
850                                break;
851                }
852        }
853       
854        return theResult;
855}
856
857/**
858 * curl init entry point.
859 *
860 * @param interp                current interpreter
861 */
862int
863CurlInit(Tcl_Interp* interp)
864{
865        CURLcode theCurlCode = curl_global_init(CURL_GLOBAL_ALL);
866        return SetResultFromCurlErrorCode(interp, theCurlCode);
867}
Note: See TracBrowser for help on using the repository browser.