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

Last change on this file since 29332 was 29332, checked in by afb@…, 12 years ago

fix bug where Last-Modified header went into output

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 22.2 KB
Line 
1/*
2 * curl.c
3 * $Id: curl.c 29332 2007-09-21 08:26:16Z afb@macports.org $
4 *
5 * Copyright (c) 2005 Paul Guyot, MacPorts Team.
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 <stdio.h>
38#include <stdlib.h>
39#include <ctype.h>
40#include <errno.h>
41
42#if HAVE_STRING_H
43#include <string.h>
44#endif
45
46#ifdef HAVE_UTIME_H
47#include <utime.h>
48#endif
49
50#include <curl/curl.h>
51
52#include <tcl.h>
53#include <tclDecls.h>
54
55#include "curl.h"
56
57/* Avoid a warning with Tcl < 8.4, even if Tcl_GetIndexFromObj's tablePtr
58probably isn't modified. */
59#if (TCL_MAJOR_VERSION > 8) || (TCL_MINOR_VERSION >= 4)
60typedef CONST char* tableEntryString;
61#else
62typedef char* tableEntryString;
63#endif
64
65/*
66 * Some compiled-in constants that we may wish to change later, given more
67 * empirical data.  These represent "best guess" values for now.
68 */
69#define _CURL_CONNECTION_TIMEOUT        ((long)(5 * 60))        /* 5 minutes */
70#define _CURL_MINIMUM_XFER_SPEED        ((long)1024)            /* 1Kb/sec */
71#define _CURL_MINIMUM_XFER_TIMEOUT      ((long)(10 * 60))       /* 10 minutes */
72
73/* ========================================================================= **
74 * Definitions
75 * ========================================================================= */
76#pragma mark Definitions
77
78/* ------------------------------------------------------------------------- **
79 * Prototypes
80 * ------------------------------------------------------------------------- */
81int SetResultFromCurlErrorCode(Tcl_Interp* interp, CURLcode inErrorCode);
82int CurlFetchCmd(Tcl_Interp* interp, int objc, Tcl_Obj* CONST objv[]);
83int CurlIsNewerCmd(Tcl_Interp* interp, int objc, Tcl_Obj* CONST objv[]);
84
85/* ========================================================================= **
86 * Entry points
87 * ========================================================================= */
88#pragma mark -
89#pragma mark Entry points
90
91/**
92 * Set the result if a libcurl error occurred return TCL_ERROR.
93 * Otherwise, set the result to "" and return TCL_OK.
94 *
95 * @param interp                pointer to the interpreter.
96 * @param inErrorCode   code of the error.
97 * @return TCL_OK if inErrorCode is 0, TCL_ERROR otherwise.
98 */
99int
100SetResultFromCurlErrorCode(Tcl_Interp* interp, CURLcode inErrorCode)
101{
102        int theResult;
103
104        switch(inErrorCode)
105        {
106                case CURLE_OK:
107                        Tcl_SetResult(interp, "", TCL_STATIC);
108                        theResult = TCL_OK;
109                        break;
110               
111                default:
112#ifdef HAVE_CURL_EASY_STRERROR
113                        Tcl_SetResult(interp, (char*) curl_easy_strerror(inErrorCode), TCL_VOLATILE);
114#else
115                        {
116                                char theErrorString[512];
117                                (void) snprintf(theErrorString, sizeof(theErrorString),
118                                        "curl error %i", inErrorCode);
119                                Tcl_SetResult(interp, theErrorString, TCL_VOLATILE);                           
120                        }
121#endif
122                        theResult = TCL_ERROR;
123        }
124       
125        return theResult;
126}
127
128/**
129 * curl fetch subcommand entry point.
130 *
131 * syntax: curl fetch [-v] [--disable-epsv] [--ignore-ssl-cert] [--remote-time] [-u userpass] [--effective-url lasturlvar] url filename
132 *
133 * @param interp                current interpreter
134 * @param objc                  number of parameters
135 * @param objv                  parameters
136 */
137int
138CurlFetchCmd(Tcl_Interp* interp, int objc, Tcl_Obj* CONST objv[])
139{
140        int theResult = TCL_OK;
141        CURL* theHandle = NULL;
142        FILE* theFile = NULL;
143
144        do {
145                long theResponseCode = 0;
146                int noprogress = 1;
147                int useepsv = 1;
148                int ignoresslcert = 0;
149                int remotetime = 0;
150                const char* theUserPassString = NULL;
151                const char* effectiveURLVarName = NULL;
152                char* effectiveURL = NULL;
153                int optioncrsr;
154                int lastoption;
155                const char* theURL;
156                const char* theFilePath;
157                long theFileTime = 0;
158                CURLcode theCurlCode;
159                struct curl_slist *headers = NULL;
160               
161                /* we might have options and then the url and the file */
162                /* let's process the options first */
163               
164                optioncrsr = 2;
165                lastoption = objc - 3;
166                while (optioncrsr <= lastoption) {
167                        /* get the option */
168                        const char* theOption = Tcl_GetString(objv[optioncrsr]);
169                       
170                        if (strcmp(theOption, "-v") == 0) {
171                                noprogress = 0;
172                        } else if (strcmp(theOption, "--disable-epsv") == 0) {
173                                useepsv = 0;
174                        } else if (strcmp(theOption, "--ignore-ssl-cert") == 0) {
175                                ignoresslcert = 1;
176                        } else if (strcmp(theOption, "--remote-time") == 0) {
177                                remotetime = 1;
178                        } else if (strcmp(theOption, "-u") == 0) {
179                                /* check we also have the parameter */
180                                if (optioncrsr < lastoption) {
181                                        optioncrsr++;
182                                        theUserPassString = Tcl_GetString(objv[optioncrsr]);
183                                } else {
184                                        Tcl_SetResult(interp,
185                                                "curl fetch: -u option requires a parameter",
186                                                TCL_STATIC);
187                                        theResult = TCL_ERROR;
188                                        break;                                 
189                                }
190                        } else if (strcmp(theOption, "--effective-url") == 0) {
191                                /* check we also have the parameter */
192                                if (optioncrsr < lastoption) {
193                                        optioncrsr++;
194                                        effectiveURLVarName = Tcl_GetString(objv[optioncrsr]);
195                                } else {
196                                        Tcl_SetResult(interp,
197                                                "curl fetch: --effective-url 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                }
240
241                /* Create the CURL handle */
242                theHandle = curl_easy_init();
243               
244                /* Setup the handle */
245                theCurlCode = curl_easy_setopt(theHandle, CURLOPT_URL, theURL);
246                if (theCurlCode != CURLE_OK) {
247                        theResult = SetResultFromCurlErrorCode(interp, theCurlCode);
248                        break;
249                }
250               
251                /* -L option */
252                theCurlCode = curl_easy_setopt(theHandle, CURLOPT_FOLLOWLOCATION, 1);
253                if (theCurlCode != CURLE_OK) {
254                        theResult = SetResultFromCurlErrorCode(interp, theCurlCode);
255                        break;
256                }
257
258                /* -f option */
259                theCurlCode = curl_easy_setopt(theHandle, CURLOPT_FAILONERROR, 1);
260                if (theCurlCode != CURLE_OK) {
261                        theResult = SetResultFromCurlErrorCode(interp, theCurlCode);
262                        break;
263                }
264
265        /* -A option */
266        theCurlCode = curl_easy_setopt(theHandle, CURLOPT_USERAGENT, "MacPorts libcurl/" LIBCURL_VERSION);
267        if (theCurlCode != CURLE_OK) {
268            theResult = SetResultFromCurlErrorCode(interp, theCurlCode);
269            break;
270        }
271
272                /* skip the header data */
273                theCurlCode = curl_easy_setopt(theHandle, CURLOPT_HEADER, 0);
274                if (theCurlCode != CURLE_OK) {
275                        theResult = SetResultFromCurlErrorCode(interp, theCurlCode);
276                        break;
277                }
278               
279                /* write to the file */
280                theCurlCode = curl_easy_setopt(theHandle, CURLOPT_WRITEDATA, theFile);
281                if (theCurlCode != CURLE_OK) {
282                        theResult = SetResultFromCurlErrorCode(interp, theCurlCode);
283                        break;
284                }
285               
286                /* we want/don't want progress */
287                theCurlCode = curl_easy_setopt(theHandle, CURLOPT_NOPROGRESS, noprogress);
288                if (theCurlCode != CURLE_OK) {
289                        theResult = SetResultFromCurlErrorCode(interp, theCurlCode);
290                        break;
291                }
292
293                /* we want/don't want to use epsv */
294                theCurlCode = curl_easy_setopt(theHandle, CURLOPT_FTP_USE_EPSV, useepsv);
295                if (theCurlCode != CURLE_OK) {
296                        theResult = SetResultFromCurlErrorCode(interp, theCurlCode);
297                        break;
298                }
299               
300                /* we may want to ignore ssl errors */
301                if (ignoresslcert) {
302                        theCurlCode = curl_easy_setopt(theHandle, CURLOPT_SSL_VERIFYPEER, (long) 0);
303                        if (theCurlCode != CURLE_OK) {
304                                theResult = SetResultFromCurlErrorCode(interp, theCurlCode);
305                                break;
306                        }
307                        theCurlCode = curl_easy_setopt(theHandle, CURLOPT_SSL_VERIFYHOST, (long) 0);
308                        if (theCurlCode != CURLE_OK) {
309                                theResult = SetResultFromCurlErrorCode(interp, theCurlCode);
310                                break;
311                        }
312                }
313
314                /* we want/don't want remote time */
315                theCurlCode = curl_easy_setopt(theHandle, CURLOPT_FILETIME, remotetime);
316                if (theCurlCode != CURLE_OK) {
317                        theResult = SetResultFromCurlErrorCode(interp, theCurlCode);
318                        break;
319                }
320
321                /* set the l/p, if any */
322                if (theUserPassString) {
323                        theCurlCode = curl_easy_setopt(theHandle, CURLOPT_USERPWD, theUserPassString);
324                        if (theCurlCode != CURLE_OK) {
325                                theResult = SetResultFromCurlErrorCode(interp, theCurlCode);
326                                break;
327                        }
328                }
329
330                /* Clear the Pragma: no-cache header */
331                headers = curl_slist_append(headers, "Pragma:");
332                theCurlCode = curl_easy_setopt(theHandle, CURLOPT_HTTPHEADER, headers);
333                if (theCurlCode != CURLE_OK) {
334                        theResult = SetResultFromCurlErrorCode(interp, theCurlCode);
335                        break;
336                }
337
338                /* actually fetch the resource */
339                theCurlCode = curl_easy_perform(theHandle);
340                if (theCurlCode != CURLE_OK) {
341                        theResult = SetResultFromCurlErrorCode(interp, theCurlCode);
342                        break;
343                }
344               
345                /* close the file */
346                (void) fclose( theFile );
347                theFile = NULL;
348               
349#ifdef HAVE_UTIME_H
350                if (remotetime) {
351                        theCurlCode = curl_easy_getinfo(theHandle, CURLINFO_FILETIME, &theFileTime);
352                        if (theFileTime > 0) {
353                                struct utimbuf times;
354                                times.actime = (time_t)theFileTime;
355                                times.modtime = (time_t)theFileTime;
356                                utime(theFilePath, &times); /* set the time we got */
357                        }
358                }
359#endif /*HAVE_UTIME_H*/
360               
361                /* free header memory */
362                curl_slist_free_all(headers);
363
364                /* If --effective-url option was given, set given variable name to last effective url used by curl */
365                if (effectiveURLVarName != NULL) {
366                        theCurlCode = curl_easy_getinfo(theHandle, CURLINFO_EFFECTIVE_URL, &effectiveURL);
367                        Tcl_SetVar(interp, effectiveURLVarName,
368                                (effectiveURL == NULL || theCurlCode != CURLE_OK) ? "" : effectiveURL,
369                                0);
370                }
371               
372                /* check everything went fine */
373                theCurlCode = curl_easy_getinfo(theHandle, CURLINFO_HTTP_CODE, &theResponseCode);
374                if (theCurlCode != CURLE_OK) {
375                        theResult = SetResultFromCurlErrorCode(interp, theCurlCode);
376                        break;
377                }
378               
379                /* we need something between 200 (incl.) and 300 (excl.).*/
380                /* (actually, we sometimes get 0 from GNU FTP servers) */
381                if (((theResponseCode != 0)  && (theResponseCode < 200))
382                        || (theResponseCode >= 300)) {
383                        char theErrorString[512];
384                        (void) snprintf(theErrorString, sizeof(theErrorString),
385                                "Download failed (code = %li)", theResponseCode);
386                        Tcl_SetResult(interp, theErrorString, TCL_VOLATILE);
387                        theResult = TCL_ERROR;
388                        break;
389                }
390               
391                /* clean up */
392                curl_easy_cleanup( theHandle );
393                theHandle = NULL;
394    } while (0);
395   
396    if (theHandle != NULL) {
397        curl_easy_cleanup( theHandle );
398    }
399    if (theFile != NULL) {
400        fclose( theFile );
401    }
402   
403        return theResult;
404}
405
406/**
407 * curl isnewer subcommand entry point.
408 *
409 * @param interp                current interpreter
410 * @param objc                  number of parameters
411 * @param objv                  parameters
412 */
413int
414CurlIsNewerCmd(Tcl_Interp* interp, int objc, Tcl_Obj* CONST objv[])
415{
416        int theResult = TCL_OK;
417        CURL* theHandle = NULL;
418        FILE* theFile = NULL;
419
420        do {
421                long theResponseCode = 0;
422                const char* theURL;
423                CURLcode theCurlCode;
424                long theModDate;
425                long userModDate;
426                               
427                /*      first (second) parameter is the url,
428                        second (third) parameter is the date */
429                if (objc != 4) {
430                        Tcl_WrongNumArgs(interp, 1, objv, "isnewer url date");
431                        theResult = TCL_ERROR;
432                        break;
433                }
434
435                /* Retrieve the url */
436                theURL = Tcl_GetString(objv[2]);
437
438                /* Get the date */
439                theResult = Tcl_GetLongFromObj(interp, objv[3], &userModDate);
440                if (theResult != TCL_OK) {
441                        break;
442                }
443               
444                /* Open the file (dev/null) */
445                theFile = fopen( "/dev/null", "a" );
446                if (theFile == NULL) {
447                        Tcl_SetResult(interp, strerror(errno), TCL_VOLATILE);
448                        theResult = TCL_ERROR;
449                }
450
451                /* Create the CURL handle */
452                theHandle = curl_easy_init();
453               
454                /* Setup the handle */
455                theCurlCode = curl_easy_setopt(theHandle, CURLOPT_URL, theURL);
456                if (theCurlCode != CURLE_OK) {
457                        theResult = SetResultFromCurlErrorCode(interp, theCurlCode);
458                        break;
459                }
460               
461                /* -L option */
462                theCurlCode = curl_easy_setopt(theHandle, CURLOPT_FOLLOWLOCATION, 1);
463                if (theCurlCode != CURLE_OK) {
464                        theResult = SetResultFromCurlErrorCode(interp, theCurlCode);
465                        break;
466                }
467
468                /* -f option */
469                theCurlCode = curl_easy_setopt(theHandle, CURLOPT_FAILONERROR, 1);
470                if (theCurlCode != CURLE_OK) {
471                        theResult = SetResultFromCurlErrorCode(interp, theCurlCode);
472                        break;
473                }
474
475                /* set timeout on connections */
476                theCurlCode = curl_easy_setopt(theHandle, CURLOPT_TIMEOUT, _CURL_CONNECTION_TIMEOUT);
477                if (theCurlCode != CURLE_OK) {
478                        theResult = SetResultFromCurlErrorCode(interp, theCurlCode);
479                        break;
480                }
481
482                /* set minimum connection speed */
483                theCurlCode = curl_easy_setopt(theHandle, CURLOPT_LOW_SPEED_LIMIT, _CURL_MINIMUM_XFER_SPEED);
484                if (theCurlCode != CURLE_OK) {
485                        theResult = SetResultFromCurlErrorCode(interp, theCurlCode);
486                        break;
487                }
488
489                /* set timeout interval for connections < min xfer speed */
490                theCurlCode = curl_easy_setopt(theHandle, CURLOPT_LOW_SPEED_TIME, _CURL_MINIMUM_XFER_TIMEOUT);
491                if (theCurlCode != CURLE_OK) {
492                        theResult = SetResultFromCurlErrorCode(interp, theCurlCode);
493                        break;
494                }
495
496                /* write to the file */
497                theCurlCode = curl_easy_setopt(theHandle, CURLOPT_WRITEDATA, theFile);
498                if (theCurlCode != CURLE_OK) {
499                        theResult = SetResultFromCurlErrorCode(interp, theCurlCode);
500                        break;
501                }
502               
503                /* save the modification date */
504                theCurlCode = curl_easy_setopt(theHandle, CURLOPT_FILETIME, 1);
505                if (theCurlCode != CURLE_OK) {
506                        theResult = SetResultFromCurlErrorCode(interp, theCurlCode);
507                        break;
508                }
509
510                /* skip the download if the file wasn't modified */
511                theCurlCode = curl_easy_setopt(theHandle, CURLOPT_TIMECONDITION, CURL_TIMECOND_IFMODSINCE);
512                if (theCurlCode != CURLE_OK) {
513                        theResult = SetResultFromCurlErrorCode(interp, theCurlCode);
514                        break;
515                }
516                theCurlCode = curl_easy_setopt(theHandle, CURLOPT_TIMEVALUE, userModDate);
517                if (theCurlCode != CURLE_OK) {
518                        theResult = SetResultFromCurlErrorCode(interp, theCurlCode);
519                        break;
520                }
521
522                /* we do not want any progress */
523                theCurlCode = curl_easy_setopt(theHandle, CURLOPT_NOPROGRESS, 1);
524                if (theCurlCode != CURLE_OK) {
525                        theResult = SetResultFromCurlErrorCode(interp, theCurlCode);
526                        break;
527                }
528               
529                /* actually fetch the resource */
530                theCurlCode = curl_easy_perform(theHandle);
531                if (theCurlCode != CURLE_OK) {
532                        theResult = SetResultFromCurlErrorCode(interp, theCurlCode);
533                        break;
534                }
535               
536                /* close the file */
537                (void) fclose( theFile );
538                theFile = NULL;
539               
540                /* check everything went fine */
541                theCurlCode = curl_easy_getinfo(theHandle, CURLINFO_HTTP_CODE, &theResponseCode);
542                if (theCurlCode != CURLE_OK) {
543                        theResult = SetResultFromCurlErrorCode(interp, theCurlCode);
544                        break;
545                }
546
547                theModDate = -1;
548
549                if (theResponseCode != 304) {
550                        /* get the modification date */
551                        theCurlCode = curl_easy_getinfo(theHandle, CURLINFO_FILETIME, &theModDate);
552                        if (theCurlCode != CURLE_OK) {
553                                theResult = SetResultFromCurlErrorCode(interp, theCurlCode);
554                                break;
555                        }
556       
557                        /* clean up */
558                        curl_easy_cleanup( theHandle );
559                        theHandle = NULL;
560       
561                        /* compare this with the date provided by user */
562                        if (theModDate < -1) {
563                                Tcl_SetResult(interp, "Couldn't get resource modification date", TCL_STATIC);
564                                theResult = TCL_ERROR;
565                                break;
566                        }
567                }
568
569                if (theModDate > userModDate) {
570                        Tcl_SetResult(interp, "1", TCL_STATIC);
571                } else {
572                        Tcl_SetResult(interp, "0", TCL_STATIC);
573                }               
574    } while (0);
575   
576    if (theHandle != NULL) {
577        curl_easy_cleanup( theHandle );
578    }
579    if (theFile != NULL) {
580        fclose( theFile );
581    }
582   
583        return theResult;
584}
585
586/**
587 * curl getsize subcommand entry point.
588 *
589 * @param interp                current interpreter
590 * @param objc                  number of parameters
591 * @param objv                  parameters
592 */
593int
594CurlGetSizeCmd(Tcl_Interp* interp, int objc, Tcl_Obj* CONST objv[])
595{
596        int theResult = TCL_OK;
597        CURL* theHandle = NULL;
598        FILE* theFile = NULL;
599
600        do {
601                char theSizeString[32];
602                const char* theURL;
603                CURLcode theCurlCode;
604                double theFileSize;
605                               
606                /*      first (second) parameter is the url */
607                if (objc != 3) {
608                        Tcl_WrongNumArgs(interp, 1, objv, "getsize url");
609                        theResult = TCL_ERROR;
610                        break;
611                }
612
613                /* Retrieve the url */
614                theURL = Tcl_GetString(objv[2]);
615
616                /* Open the file (dev/null) */
617                theFile = fopen( "/dev/null", "a" );
618                if (theFile == NULL) {
619                        Tcl_SetResult(interp, strerror(errno), TCL_VOLATILE);
620                        theResult = TCL_ERROR;
621                }
622
623                /* Create the CURL handle */
624                theHandle = curl_easy_init();
625               
626                /* Setup the handle */
627                theCurlCode = curl_easy_setopt(theHandle, CURLOPT_URL, theURL);
628                if (theCurlCode != CURLE_OK) {
629                        theResult = SetResultFromCurlErrorCode(interp, theCurlCode);
630                        break;
631                }
632               
633                /* -L option */
634                theCurlCode = curl_easy_setopt(theHandle, CURLOPT_FOLLOWLOCATION, 1);
635                if (theCurlCode != CURLE_OK) {
636                        theResult = SetResultFromCurlErrorCode(interp, theCurlCode);
637                        break;
638                }
639
640                /* -f option */
641                theCurlCode = curl_easy_setopt(theHandle, CURLOPT_FAILONERROR, 1);
642                if (theCurlCode != CURLE_OK) {
643                        theResult = SetResultFromCurlErrorCode(interp, theCurlCode);
644                        break;
645                }
646
647                /* set timeout on connections */
648                theCurlCode = curl_easy_setopt(theHandle, CURLOPT_TIMEOUT, _CURL_CONNECTION_TIMEOUT);
649                if (theCurlCode != CURLE_OK) {
650                        theResult = SetResultFromCurlErrorCode(interp, theCurlCode);
651                        break;
652                }
653
654                /* set minimum connection speed */
655                theCurlCode = curl_easy_setopt(theHandle, CURLOPT_LOW_SPEED_LIMIT, _CURL_MINIMUM_XFER_SPEED);
656                if (theCurlCode != CURLE_OK) {
657                        theResult = SetResultFromCurlErrorCode(interp, theCurlCode);
658                        break;
659                }
660
661                /* set timeout interval for connections < min xfer speed */
662                theCurlCode = curl_easy_setopt(theHandle, CURLOPT_LOW_SPEED_TIME, _CURL_MINIMUM_XFER_TIMEOUT);
663                if (theCurlCode != CURLE_OK) {
664                        theResult = SetResultFromCurlErrorCode(interp, theCurlCode);
665                        break;
666                }
667
668                /* write to the file */
669                theCurlCode = curl_easy_setopt(theHandle, CURLOPT_WRITEDATA, theFile);
670                if (theCurlCode != CURLE_OK) {
671                        theResult = SetResultFromCurlErrorCode(interp, theCurlCode);
672                        break;
673                }
674
675                /* skip the header data */
676                theCurlCode = curl_easy_setopt(theHandle, CURLOPT_HEADER, 0);
677                if (theCurlCode != CURLE_OK) {
678                        theResult = SetResultFromCurlErrorCode(interp, theCurlCode);
679                        break;
680                }
681               
682                /* skip the body data */
683                theCurlCode = curl_easy_setopt(theHandle, CURLOPT_NOBODY, 1);
684                if (theCurlCode != CURLE_OK) {
685                        theResult = SetResultFromCurlErrorCode(interp, theCurlCode);
686                        break;
687                }
688
689                /* we do not want any progress */
690                theCurlCode = curl_easy_setopt(theHandle, CURLOPT_NOPROGRESS, 1);
691                if (theCurlCode != CURLE_OK) {
692                        theResult = SetResultFromCurlErrorCode(interp, theCurlCode);
693                        break;
694                }
695               
696                /* actually fetch the resource */
697                theCurlCode = curl_easy_perform(theHandle);
698                if (theCurlCode != CURLE_OK) {
699                        theResult = SetResultFromCurlErrorCode(interp, theCurlCode);
700                        break;
701                }
702               
703                /* close the file */
704                (void) fclose( theFile );
705                theFile = NULL;
706
707                theFileSize = 0.0;
708
709                /* get the file size */
710                theCurlCode = curl_easy_getinfo(theHandle, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &theFileSize);
711                if (theCurlCode != CURLE_OK) {
712                        theResult = SetResultFromCurlErrorCode(interp, theCurlCode);
713                        break;
714                }
715       
716                /* clean up */
717                curl_easy_cleanup( theHandle );
718                theHandle = NULL;
719
720                (void) snprintf(theSizeString, sizeof(theSizeString),
721                        "%.0f", theFileSize);
722                Tcl_SetResult(interp, theSizeString, TCL_VOLATILE);
723    } while (0);
724   
725    if (theHandle != NULL) {
726        curl_easy_cleanup( theHandle );
727    }
728    if (theFile != NULL) {
729        fclose( theFile );
730    }
731   
732        return theResult;
733}
734
735/**
736 * curl command entry point.
737 *
738 * @param clientData    custom data (ignored)
739 * @param interp                current interpreter
740 * @param objc                  number of parameters
741 * @param objv                  parameters
742 */
743int
744CurlCmd(
745                ClientData clientData UNUSED,
746                Tcl_Interp* interp,
747                int objc, 
748                Tcl_Obj* CONST objv[])
749{
750    typedef enum {
751        kCurlFetch,
752        kCurlIsNewer,
753        kCurlGetSize
754    } EOption;
755   
756        static tableEntryString options[] = {
757                "fetch", "isnewer", "getsize", NULL
758        };
759        int theResult = TCL_OK;
760    EOption theOptionIndex;
761
762        if (objc < 3) {
763                Tcl_WrongNumArgs(interp, 1, objv, "option ?arg ...?");
764                return TCL_ERROR;
765        }
766
767        theResult = Tcl_GetIndexFromObj(
768                                interp,
769                                objv[1],
770                                options,
771                                "option",
772                                0,
773                                (int*) &theOptionIndex);
774        if (theResult == TCL_OK) {
775                switch (theOptionIndex)
776                {
777                        case kCurlFetch:
778                                theResult = CurlFetchCmd(interp, objc, objv);
779                                break;
780
781                        case kCurlIsNewer:
782                                theResult = CurlIsNewerCmd(interp, objc, objv);
783                                break;
784
785                        case kCurlGetSize:
786                                theResult = CurlGetSizeCmd(interp, objc, objv);
787                                break;
788                }
789        }
790       
791        return theResult;
792}
793
794/**
795 * curl init entry point.
796 *
797 * @param interp                current interpreter
798 */
799int
800CurlInit(Tcl_Interp* interp)
801{
802        CURLcode theCurlCode = curl_global_init(CURL_GLOBAL_ALL);
803        return SetResultFromCurlErrorCode(interp, theCurlCode);
804}
805
806/* ============================================================== **
807** As of next Thursday, UNIX will be flushed in favor of TOPS-10. **
808** Please update your programs.                                   **
809** ============================================================== */
Note: See TracBrowser for help on using the repository browser.