ftplib.c

Go to the documentation of this file.
00001 /********************************************************************\
00002 
00003   Name:         ftplib.c
00004   Created by:   Originally written by Oleg Orel (orel@lpuds.oea.ihep.su),
00005                 translated from UNIX to NT by Urs Rohrer (rohrer@psi.ch),
00006                 simplified and adapted for MIDAS by Stefan Ritt.
00007 
00008   Contents:     File Transfer Protocol library
00009 
00010   $Id: ftplib.c 4839 2010-09-27 22:44:59Z amaudruz $
00011 
00012 \********************************************************************/
00013 
00014 #include "midas.h"
00015 #include "msystem.h"
00016 #include "ftplib.h"
00017 
00018 #ifndef FTP_SUCCESS
00019 #define FTP_SUCCESS                   1
00020 #define FTP_NET_ERROR               802
00021 #define FTP_FILE_ERROR              803
00022 #define FTP_RESPONSE_ERROR          804
00023 #define FTP_INVALID_ARG             805
00024 #endif
00025 
00026 static char bars[] = "/-\\|";
00027 int (*ftp_debug_func) (char *message);
00028 int (*ftp_error_func) (char *message);
00029 
00030 /*------------------------------------------------------------------*/
00031 
00032 void ftp_debug(int (*debug_func) (char *message), int (*error_func) (char *message))
00033 /* set message display functions for debug and error messages */
00034 {
00035    ftp_debug_func = debug_func;
00036    ftp_error_func = error_func;
00037 }
00038 
00039 /*------------------------------------------------------------------*/
00040 
00041 int ftp_connect(FTP_CON ** con, const char *host_name, unsigned short port)
00042 /* Connect to a FTP server on a host at a given port (usually 21).
00043    Return a FTP_CON structure if successful */
00044 {
00045    struct sockaddr_in bind_addr;
00046    int sock;
00047    char str[4000];
00048    int status;
00049    struct hostent *phe;
00050 
00051    *con = NULL;
00052 
00053 #ifdef OS_WINNT
00054    {
00055       WSADATA WSAData;
00056 
00057       /* Start windows sockets */
00058       if (WSAStartup(MAKEWORD(1, 1), &WSAData) != 0)
00059          return FTP_NET_ERROR;
00060    }
00061 #endif
00062 
00063    if ((sock = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
00064       if (ftp_error_func)
00065          ftp_error_func("cannot create socket");
00066       return FTP_NET_ERROR;
00067    }
00068 
00069    /* connect to remote node */
00070    memset(&bind_addr, 0, sizeof(bind_addr));
00071    bind_addr.sin_family = AF_INET;
00072    bind_addr.sin_addr.s_addr = 0;
00073    bind_addr.sin_port = htons(port);
00074 
00075 #ifdef OS_VXWORKS
00076    {
00077       INT host_addr;
00078 
00079       host_addr = hostGetByName(host_name);
00080       memcpy((char *) &(bind_addr.sin_addr), &host_addr, 4);
00081    }
00082 #else
00083    phe = gethostbyname(host_name);
00084    if (phe == NULL) {
00085       if (ftp_error_func)
00086          ftp_error_func("cannot get host name");
00087       return RPC_NET_ERROR;
00088    }
00089    memcpy((char *) &(bind_addr.sin_addr), phe->h_addr, phe->h_length);
00090 #endif
00091 
00092 #ifdef OS_UNIX
00093    do {
00094       status = connect(sock, (struct sockaddr *) &bind_addr, sizeof(bind_addr));
00095 
00096       /* don't return if an alarm signal was cought */
00097    } while (status == -1 && errno == EINTR);
00098 #else
00099    status = connect(sock, (struct sockaddr *) &bind_addr, sizeof(bind_addr));
00100 #endif
00101 
00102    if (status != 0) {
00103       sprintf(str, "cannot connect to host %s, port %d", host_name, port);
00104       if (ftp_error_func)
00105          ftp_error_func(str);
00106       return FTP_NET_ERROR;
00107    }
00108 
00109 
00110    *con = (FTP_CON *) malloc(sizeof(FTP_CON));
00111    (*con)->sock = sock;
00112    (*con)->data = 0;
00113 
00114    memset(str, 0, sizeof(str));
00115    status = ftp_get_message(*con, str);
00116 
00117    /* check for status */
00118    if (status == FTP_QUIT || !ftp_good(status, 120, 220, EOF)) {
00119       closesocket(sock);
00120       free(*con);
00121       return FTP_NET_ERROR;
00122    }
00123 
00124    return FTP_SUCCESS;
00125 }
00126 
00127 /*------------------------------------------------------------------*/
00128 
00129 int ftp_send_message(FTP_CON * con, char *message)
00130 /* send a message to a FTP server */
00131 {
00132    if (send(con->sock, message, strlen(message), 0) == -1)
00133       return FTP_NET_ERROR;
00134 
00135    if (send(con->sock, "\r\n", 2, 0) == -1)
00136       return FTP_NET_ERROR;
00137 
00138    if (ftp_debug_func != NULL)
00139       ftp_debug_func(message);
00140 
00141    return FTP_SUCCESS;
00142 }
00143 
00144 /*------------------------------------------------------------------*/
00145 
00146 int ftp_command(FTP_CON * con, const char *command, const char *param, ...)
00147 /* execute FTP command, check for return codes */
00148 {
00149    va_list args;
00150    char str[256];
00151    int status, code;
00152    BOOL result_ok;
00153 
00154    /* compose command string */
00155    sprintf(str, command, param);
00156 
00157    /* send command */
00158    if (ftp_send_message(con, str) != FTP_SUCCESS)
00159       return FTP_NET_ERROR;
00160 
00161    /* read reply */
00162    status = ftp_get_message(con, str);
00163    if (status == FTP_QUIT)
00164       return FTP_NET_ERROR;
00165 
00166    /* check reply code */
00167    va_start(args, param);
00168 
00169    result_ok = FALSE;
00170    do {
00171       code = va_arg(args, int);
00172       if (code == EOF)
00173          break;
00174 
00175       if (code == status)
00176          result_ok = TRUE;
00177 
00178    } while (!result_ok);
00179 
00180    va_end(args);
00181 
00182    if (!result_ok) {
00183       if (ftp_error_func != NULL)
00184          ftp_error_func(str);
00185 
00186       return FTP_RESPONSE_ERROR;
00187    }
00188 
00189    return -status;
00190 }
00191 
00192 /*------------------------------------------------------------------*/
00193 
00194 int ftp_get_message(FTP_CON * con, char *message)
00195 /* read message from FTP server */
00196 {
00197    int i;
00198 
00199    for (i = 0;; i++) {
00200       if (recv(con->sock, &message[i], 1, 0) != 1)
00201          return FTP_QUIT;
00202 
00203       if (i > 1 && message[i] == 10 && message[i - 1] == 13)
00204          break;
00205    }
00206 
00207    message[i - 1] = 0;
00208 
00209    con->err_no = atoi(message);
00210 
00211    if (ftp_debug_func != NULL)
00212       ftp_debug_func(message);
00213 
00214    /* check for continuation message */
00215    if (message[3] == '-')
00216       ftp_get_message(con, message + strlen(message));
00217 
00218    return con->err_no;
00219 }
00220 
00221 /*------------------------------------------------------------------*/
00222 
00223 BOOL ftp_good(int number, ...)
00224 /* check if number matches any code from argument list */
00225 {
00226    va_list args;
00227    BOOL result;
00228    int code;
00229 
00230    va_start(args, number);
00231    result = FALSE;
00232 
00233    do {
00234       code = va_arg(args, int);
00235       if (code == EOF)
00236          break;
00237 
00238       if (code == number)
00239          result = TRUE;
00240 
00241    } while (!result);
00242 
00243    va_end(args);
00244 
00245    return result;
00246 }
00247 
00248 /*------------------------------------------------------------------*/
00249 
00250 int ftp_data(FTP_CON * con, const char *command, const char *file)
00251 /* open data socket */
00252 {
00253    struct sockaddr_in data, from;
00254    struct hostent *host;
00255    char host_name[256];
00256    int listen_socket, data_socket;
00257    unsigned int len = sizeof(data), fromlen = sizeof(from);
00258    int one = 1, status;
00259    char *a, *b;
00260 
00261    memset(&data, 0, sizeof(data));
00262    memset(&from, 0, sizeof(from));
00263 
00264    if (gethostname(host_name, sizeof(host_name)) == -1)
00265       return FTP_NET_ERROR;
00266 
00267    if ((host = (struct hostent *) gethostbyname(host_name)) == 0)
00268       return FTP_NET_ERROR;
00269 
00270    if ((listen_socket = socket(AF_INET, SOCK_STREAM, 0)) == -1)
00271       return FTP_NET_ERROR;
00272 
00273    if (setsockopt(listen_socket, SOL_SOCKET, SO_REUSEADDR,
00274                   (char *) &one, sizeof(one)) < 0) {
00275       closesocket(listen_socket);
00276       return FTP_NET_ERROR;
00277    }
00278 
00279    data.sin_family = AF_INET;
00280    data.sin_port = htons(0);
00281    data.sin_addr.s_addr = *(unsigned long *) *(host->h_addr_list);
00282 
00283    if (bind(listen_socket, (struct sockaddr *) &data, sizeof(data)) == -1) {
00284       closesocket(listen_socket);
00285       return FTP_NET_ERROR;
00286    }
00287 
00288 #ifdef OS_WINNT
00289    if (getsockname(listen_socket, (struct sockaddr *) &data, (int *)&len) < 0) {
00290 #else
00291    if (getsockname(listen_socket, (struct sockaddr *) &data, &len) < 0) {
00292 #endif
00293       closesocket(listen_socket);
00294       return FTP_NET_ERROR;
00295    }
00296 
00297    if (listen(listen_socket, 1) != 0) {
00298       closesocket(listen_socket);
00299       return FTP_NET_ERROR;
00300    }
00301 
00302    a = (char *) &data.sin_addr;
00303    b = (char *) &data.sin_port;
00304 
00305    status = ftp_port(con, FTP_CUT(a[0]), FTP_CUT(a[1]), FTP_CUT(a[2]),
00306                      FTP_CUT(a[3]), FTP_CUT(b[0]), FTP_CUT(b[1]));
00307    if (status != FTP_SUCCESS)
00308       return FTP_NET_ERROR;
00309 
00310    status = ftp_command(con, command, file, 200, 120, 150, 125, 250, EOF);
00311    if (status >= 0)
00312       return status;
00313 
00314 #ifdef OS_WINNT
00315    data_socket = accept(listen_socket, (struct sockaddr *) &from, (int *)&fromlen);
00316 #else
00317    data_socket = accept(listen_socket, (struct sockaddr *) &from, &fromlen);
00318 #endif
00319 
00320    if (data_socket == -1) {
00321       closesocket(listen_socket);
00322       return FTP_NET_ERROR;
00323    }
00324 
00325    closesocket(listen_socket);
00326 
00327    con->data = data_socket;
00328 
00329    return status;
00330 }
00331 
00332 /*------------------------------------------------------------------*/
00333 
00334 int ftp_close(FTP_CON * con)
00335 /* close data connection */
00336 {
00337    char str[256];
00338 
00339    closesocket(con->data);
00340 
00341    return ftp_get_message(con, str);
00342 }
00343 
00344 /*------------------------------------------------------------------*/
00345 
00346 int ftp_send(int sock, char *buffer, int n_bytes_to_write)
00347 /* send number of bytes over socket */
00348 {
00349    int n_bytes_left, n_written;
00350 
00351    n_bytes_left = n_bytes_to_write;
00352 
00353    while (n_bytes_left > 0) {
00354       n_written = send(sock, buffer, n_bytes_left > 8192 ? 8192 : n_bytes_left, 0);
00355       if (n_written <= 0)
00356          return n_bytes_to_write - n_bytes_left;
00357 
00358       n_bytes_left -= n_written;
00359       buffer += n_written;
00360    }
00361 
00362    return n_bytes_to_write;
00363 }
00364 
00365 /*------------------------------------------------------------------*/
00366 
00367 int ftp_receive(int sock, char *buffer, int bsize)
00368 /* receive buffer from socket, return number of received bytes */
00369 {
00370    int count, i;
00371 
00372    i = 0;
00373    while (TRUE) {
00374       count = recv(sock, buffer + i, bsize - 1 - i, 0);
00375       if (count <= 0)
00376          return i;
00377 
00378       i += count;
00379       buffer[i] = 0;
00380 
00381       if (i >= bsize - 1)
00382          return i;
00383    }
00384 }
00385 
00386 /*------------------------------------------------------------------*/
00387 
00388 int ftp_login(FTP_CON ** con, const char *host, unsigned short port,
00389               const char *user, const char *password, const char *account)
00390 /* FTP login with username and password */
00391 {
00392    int status;
00393 
00394    status = ftp_connect(con, host, port);
00395    if (status != FTP_SUCCESS)
00396       return status;
00397 
00398    status = ftp_user(*con, user);
00399    if (status >= 0)
00400       return status;
00401 
00402    if (status == -230)
00403       return status;
00404 
00405    if (status == -332) {
00406       if (account == NULL)
00407          return FTP_NET_ERROR;
00408 
00409       status = ftp_account(*con, account);
00410       if (status < 1)
00411          return status;
00412 
00413       if (status == -230)
00414          return status;
00415    }
00416 
00417    return ftp_password(*con, password);
00418 }
00419 
00420 /*------------------------------------------------------------------*/
00421 
00422 int ftp_bye(FTP_CON * con)
00423 /* disconnect from FTP server */
00424 {
00425    char str[256];
00426 
00427    ftp_send_message(con, "QUIT");
00428    ftp_get_message(con, str);
00429 
00430    closesocket(con->sock);
00431    free(con);
00432 
00433    return FTP_SUCCESS;
00434 }
00435 
00436 /*------------------------------------------------------------------*/
00437 
00438 int ftp_port(FTP_CON * con, int a, int b, int c, int d, int e, int f)
00439 /* set port for data connection */
00440 {
00441    char cmd[256];
00442    int status;
00443 
00444    sprintf(cmd, "PORT %d,%d,%d,%d,%d,%d", a, b, c, d, e, f);
00445    if (ftp_send_message(con, cmd) != FTP_SUCCESS)
00446       return FTP_NET_ERROR;
00447 
00448    status = ftp_get_message(con, cmd);
00449    if (status == FTP_QUIT)
00450       return FTP_QUIT;
00451 
00452    if (!ftp_good(status, 200, EOF))
00453       return FTP_NET_ERROR;
00454 
00455    return FTP_SUCCESS;
00456 }
00457 
00458 /*------------------------------------------------------------------*/
00459 
00460 int ftp_move(FTP_CON * con, const char *oldname, const char *newname)
00461 /* move/rename file */
00462 {
00463    int status;
00464 
00465    status = ftp_command(con, "RNFR %s", oldname, 200, 350, EOF);
00466 
00467    if (status < 0)
00468       return ftp_command(con, "RNTO %s", newname, 200, 250, EOF);
00469    else
00470       return status;
00471 }
00472 
00473 /*------------------------------------------------------------------*/
00474 
00475 int ftp_get(FTP_CON * con, const char *local_name, const char *remote_name)
00476 /* get file */
00477 {
00478    int fh;
00479    int status;
00480    char buff[8192];
00481    char str[256];
00482    int count, i;
00483    long total = 0;
00484    DWORD start, stop;
00485 
00486    if (ftp_open_read(con, remote_name) >= 0)
00487       return con->err_no;
00488 
00489    if ((fh = open(local_name, O_RDWR | O_CREAT | O_TRUNC | O_BINARY, 0644)) == -1)
00490       return FTP_FILE_ERROR;
00491 
00492    start = ss_millitime();
00493 
00494    while ((count = ftp_receive(con->data, buff, sizeof(buff))) > 0) {
00495       total += write(fh, buff, count);
00496       i = 0;
00497       if (ftp_debug_func != NULL) {
00498          printf("%c\r", bars[(i++) % 4]);
00499          fflush(stdout);
00500       }
00501    }
00502 
00503    close(fh);
00504    stop = ss_millitime();
00505 
00506    status = ftp_close(con);
00507    if (ftp_debug_func != NULL) {
00508       sprintf(str, "%ld bytes received in %1.2f seconds (%1.2lf kB/sec).",
00509               total, (stop - start) / 1000.0, total / 1024.0 / ((stop - start) / 1000.0));
00510       ftp_debug_func(str);
00511    }
00512 
00513    return status;
00514 }
00515 
00516 /*------------------------------------------------------------------*/
00517 
00518 int ftp_put(FTP_CON * con, const char *local_name, const char *remote_name)
00519 /* put file */
00520 {
00521    int fh;
00522    int status;
00523    char buff[8193];
00524    char str[256];
00525    int count, i = 0;
00526    long total = 0;
00527    DWORD start, stop;
00528 
00529    if (ftp_open_write(con, remote_name) >= 0)
00530       return con->err_no;
00531 
00532    if ((fh = open(local_name, O_BINARY)) == -1)
00533       return FTP_FILE_ERROR;
00534 
00535    start = ss_millitime();
00536 
00537    while ((count = read(fh, buff, 8192)) > 0) {
00538       total += ftp_send(con->data, buff, count);
00539       if (ftp_debug_func != NULL) {
00540          printf("%c\r", bars[(i++) % 4]);
00541          fflush(stdout);
00542       }
00543    }
00544 
00545    close(fh);
00546    stop = ss_millitime();
00547 
00548    status = ftp_close(con);
00549    if (ftp_debug_func != NULL) {
00550       sprintf(str, "%ld bytes sent in %1.2f seconds (%1.2lf kB/sec).",
00551               total, (stop - start) / 1000.0, total / 1024.0 / ((stop - start) / 1000.0));
00552       ftp_debug_func(str);
00553    }
00554 
00555    return status;
00556 }
00557 
00558 /*------------------------------------------------------------------*/
00559 
00560 char *ftp_pwd(FTP_CON * con)
00561 /* show present working directory */
00562 {
00563    static char str[256];
00564    char tmp[256];
00565    int status;
00566 
00567    str[0] = 0;
00568    if (ftp_send_message(con, "PWD") != FTP_SUCCESS)
00569       return str;
00570 
00571    status = ftp_get_message(con, tmp);
00572 
00573    if (status != 257) {
00574       if (ftp_error_func != NULL)
00575          ftp_error_func(tmp);
00576 
00577       return str;
00578    }
00579 
00580    sscanf(tmp, "%*[^\"]%*c%[^\"]%*s", str);
00581 
00582    return str;
00583 }
00584 
00585 /*------------------------------------------------------------------*/
00586 
00587 int ftp_dir(FTP_CON * con, const char *file)
00588 /* display directory */
00589 {
00590    char command[256], buffer[8192];
00591 
00592    if (file == NULL || *file == '\0')
00593       strcpy(command, "LIST");
00594    else
00595       sprintf(command, "LIST %s", file);
00596 
00597    if (ftp_data(con, command, "") >= 0)
00598       return con->err_no;
00599 
00600    while (ftp_receive(con->data, buffer, sizeof(buffer)))
00601       printf(buffer);
00602 
00603    return ftp_close(con);
00604 }
00605 
00606 /*------------------------------------------------------------------*/

Midas DOC Version 3.0.0 ---- PSI Stefan Ritt ----
Contributions: Pierre-Andre Amaudruz - Sergio Ballestrero - Suzannah Daviel - Doxygen - Peter Green - Qing Gu - Greg Hackman - Gertjan Hofman - Paul Knowles - Exaos Lee - Rudi Meier - Glenn Moloney - Dave Morris - John M O'Donnell - Konstantin Olchanski - Renee Poutissou - Tamsen Schurman - Andreas Suter - Jan M.Wouters - Piotr Adam Zolnierczuk