Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can fopen be used to open the URL

Is fopen("tftp://1.1.1.1/file.txt","rb"); a valid statement? Can urls be opened using fopen in C programming?

like image 283
foo_l Avatar asked Oct 30 '14 09:10

foo_l


Video Answer


3 Answers

No, but you can use libcurl, an example:

#include <stdio.h>
#include <curl/curl.h>

/*
 * This is an example showing how to get a single file from an FTP server.
 * It delays the actual destination file creation until the first write
 * callback so that it won't create an empty file in case the remote file
 * doesn't exist or something else fails.
 */ 

struct FtpFile {
  const char *filename;
  FILE *stream;
};

static size_t my_fwrite(void *buffer, size_t size, size_t nmemb, void *stream)
{
  struct FtpFile *out=(struct FtpFile *)stream;
  if(out && !out->stream) {
    /* open file for writing */ 
    out->stream=fopen(out->filename, "wb");
    if(!out->stream)
      return -1; /* failure, can't open file to write */ 
  }
  return fwrite(buffer, size, nmemb, out->stream);
}


int main(void)
{
  CURL *curl;
  CURLcode res;
  struct FtpFile ftpfile={
    "curl.tar.gz", /* name to store the file as if succesful */ 
    NULL
  };

  curl_global_init(CURL_GLOBAL_DEFAULT);

  curl = curl_easy_init();
  if(curl) {
    /*
     * You better replace the URL with one that works!
     */ 
    curl_easy_setopt(curl, CURLOPT_URL,
                     "ftp://ftp.example.com/pub/www/utilities/curl/curl-7.9.2.tar.gz");
    /* Define our callback to get called when there's data to be written */ 
    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, my_fwrite);
    /* Set a pointer to our struct to pass to the callback */ 
    curl_easy_setopt(curl, CURLOPT_WRITEDATA, &ftpfile);

    /* Switch on full protocol/debug output */ 
    curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);

    res = curl_easy_perform(curl);

    /* always cleanup */ 
    curl_easy_cleanup(curl);

    if(CURLE_OK != res) {
      /* we failed */ 
      fprintf(stderr, "curl told us %d\n", res);
    }
  }

  if(ftpfile.stream)
    fclose(ftpfile.stream); /* close the local file */ 

  curl_global_cleanup();

  return 0;
}

Or (as pointed out by @Paul) you can pipe a process (E.g.: wget url) with popen:

#include <stdio.h>

FILE *popen(const char *command, const char *mode);
int pclose(FILE *stream);

int main(void)
{
    /* wget -q = silent mode */
    FILE *cmd = popen("wget -q -O - ftp://debian.org/debian-security/README.security", "r");
    char result[1024];

    while (fgets(result, sizeof(result), cmd) != NULL)
        printf("%s", result);
    pclose(cmd);
    return 0;
}
like image 88
David Ranieri Avatar answered Sep 18 '22 04:09

David Ranieri


The fopen in <stdio.h> doesn't do that.

However, nothing prevents someone from writing a function called fopen() that does something else.

FILE *popen(const char *command, const char *mode) can be used to spawn a process running an appropriate command line tool such as tftp or wget, and thereby accomplish this downloading of a remote resource into a file descriptor accessible from C code. The syntax for a popen() call is very similar to what you have shown. It is missing the program name for the download utility, though. A bare url or ftp address won't work for popen().

See:

  • fopen man page
  • popen man page

Also note:

The PHP language version of fopen() does open bare URLs. But PHP != C

like image 21
Paul Avatar answered Sep 21 '22 04:09

Paul


It's not that easy as simply using fopen but it can be done.

You need to use libcurl. Take a look here.

From the site:

/*****************************************************************************
 *
 * This example requires libcurl 7.9.7 or later.
 */ 

#include <stdio.h>
#include <string.h>
#ifndef WIN32
#include <sys/time.h>
#endif
#include <stdlib.h>
#include <errno.h>

#include <curl/curl.h>

enum fcurl_type_e {
  CFTYPE_NONE=0,
  CFTYPE_FILE=1,
  CFTYPE_CURL=2
};

struct fcurl_data
{
  enum fcurl_type_e type;     /* type of handle */ 
  union {
    CURL *curl;
    FILE *file;
  } handle;                   /* handle */ 

  char *buffer;               /* buffer to store cached data*/ 
  size_t buffer_len;          /* currently allocated buffers length */ 
  size_t buffer_pos;          /* end of data in buffer*/ 
  int still_running;          /* Is background url fetch still in progress */ 
};

typedef struct fcurl_data URL_FILE;

/* exported functions */ 
URL_FILE *url_fopen(const char *url,const char *operation);
int url_fclose(URL_FILE *file);
int url_feof(URL_FILE *file);
size_t url_fread(void *ptr, size_t size, size_t nmemb, URL_FILE *file);
char * url_fgets(char *ptr, size_t size, URL_FILE *file);
void url_rewind(URL_FILE *file);

/* we use a global one for convenience */ 
CURLM *multi_handle;

/* curl calls this routine to get more data */ 
static size_t write_callback(char *buffer,
                             size_t size,
                             size_t nitems,
                             void *userp)
{
  char *newbuff;
  size_t rembuff;

  URL_FILE *url = (URL_FILE *)userp;
  size *= nitems;

  rembuff=url->buffer_len - url->buffer_pos; /* remaining space in buffer */ 

  if(size > rembuff) {
    /* not enough space in buffer */ 
    newbuff=realloc(url->buffer,url->buffer_len + (size - rembuff));
    if(newbuff==NULL) {
      fprintf(stderr,"callback buffer grow failed\n");
      size=rembuff;
    }
    else {
      /* realloc suceeded increase buffer size*/ 
      url->buffer_len+=size - rembuff;
      url->buffer=newbuff;
    }
  }

  memcpy(&url->buffer[url->buffer_pos], buffer, size);
  url->buffer_pos += size;

  return size;
}

/* use to attempt to fill the read buffer up to requested number of bytes */ 
static int fill_buffer(URL_FILE *file, size_t want)
{
  fd_set fdread;
  fd_set fdwrite;
  fd_set fdexcep;
  struct timeval timeout;
  int rc;

  /* only attempt to fill buffer if transactions still running and buffer
   * doesnt exceed required size already
   */ 
  if((!file->still_running) || (file->buffer_pos > want))
    return 0;

  /* attempt to fill buffer */ 
  do {
    int maxfd = -1;
    long curl_timeo = -1;

    FD_ZERO(&fdread);
    FD_ZERO(&fdwrite);
    FD_ZERO(&fdexcep);

    /* set a suitable timeout to fail on */ 
    timeout.tv_sec = 60; /* 1 minute */ 
    timeout.tv_usec = 0;

    curl_multi_timeout(multi_handle, &curl_timeo);
    if(curl_timeo >= 0) {
      timeout.tv_sec = curl_timeo / 1000;
      if(timeout.tv_sec > 1)
        timeout.tv_sec = 1;
      else
        timeout.tv_usec = (curl_timeo % 1000) * 1000;
    }

    /* get file descriptors from the transfers */ 
    curl_multi_fdset(multi_handle, &fdread, &fdwrite, &fdexcep, &maxfd);

    /* In a real-world program you OF COURSE check the return code of the
       function calls.  On success, the value of maxfd is guaranteed to be
       greater or equal than -1.  We call select(maxfd + 1, ...), specially
       in case of (maxfd == -1), we call select(0, ...), which is basically
       equal to sleep. */ 

    rc = select(maxfd+1, &fdread, &fdwrite, &fdexcep, &timeout);

    switch(rc) {
    case -1:
      /* select error */ 
      break;

    case 0:
    default:
      /* timeout or readable/writable sockets */ 
      curl_multi_perform(multi_handle, &file->still_running);
      break;
    }
  } while(file->still_running && (file->buffer_pos < want));
  return 1;
}

/* use to remove want bytes from the front of a files buffer */ 
static int use_buffer(URL_FILE *file,int want)
{
  /* sort out buffer */ 
  if((file->buffer_pos - want) <=0) {
    /* ditch buffer - write will recreate */ 
    if(file->buffer)
      free(file->buffer);

    file->buffer=NULL;
    file->buffer_pos=0;
    file->buffer_len=0;
  }
  else {
    /* move rest down make it available for later */ 
    memmove(file->buffer,
            &file->buffer[want],
            (file->buffer_pos - want));

    file->buffer_pos -= want;
  }
  return 0;
}

URL_FILE *url_fopen(const char *url,const char *operation)
{
  /* this code could check for URLs or types in the 'url' and
     basicly use the real fopen() for standard files */ 

  URL_FILE *file;
  (void)operation;

  file = malloc(sizeof(URL_FILE));
  if(!file)
    return NULL;

  memset(file, 0, sizeof(URL_FILE));

  if((file->handle.file=fopen(url,operation)))
    file->type = CFTYPE_FILE; /* marked as URL */ 

  else {
    file->type = CFTYPE_CURL; /* marked as URL */ 
    file->handle.curl = curl_easy_init();

    curl_easy_setopt(file->handle.curl, CURLOPT_URL, url);
    curl_easy_setopt(file->handle.curl, CURLOPT_WRITEDATA, file);
    curl_easy_setopt(file->handle.curl, CURLOPT_VERBOSE, 0L);
    curl_easy_setopt(file->handle.curl, CURLOPT_WRITEFUNCTION, write_callback);

    if(!multi_handle)
      multi_handle = curl_multi_init();

    curl_multi_add_handle(multi_handle, file->handle.curl);

    /* lets start the fetch */ 
    curl_multi_perform(multi_handle, &file->still_running);

    if((file->buffer_pos == 0) && (!file->still_running)) {
      /* if still_running is 0 now, we should return NULL */ 

      /* make sure the easy handle is not in the multi handle anymore */ 
      curl_multi_remove_handle(multi_handle, file->handle.curl);

      /* cleanup */ 
      curl_easy_cleanup(file->handle.curl);

      free(file);

      file = NULL;
    }
  }
  return file;
}

int url_fclose(URL_FILE *file)
{
  int ret=0;/* default is good return */ 

  switch(file->type) {
  case CFTYPE_FILE:
    ret=fclose(file->handle.file); /* passthrough */ 
    break;

  case CFTYPE_CURL:
    /* make sure the easy handle is not in the multi handle anymore */ 
    curl_multi_remove_handle(multi_handle, file->handle.curl);

    /* cleanup */ 
    curl_easy_cleanup(file->handle.curl);
    break;

  default: /* unknown or supported type - oh dear */ 
    ret=EOF;
    errno=EBADF;
    break;
  }

  if(file->buffer)
    free(file->buffer);/* free any allocated buffer space */ 

  free(file);

  return ret;
}

int url_feof(URL_FILE *file)
{
  int ret=0;

  switch(file->type) {
  case CFTYPE_FILE:
    ret=feof(file->handle.file);
    break;

  case CFTYPE_CURL:
    if((file->buffer_pos == 0) && (!file->still_running))
      ret = 1;
    break;

  default: /* unknown or supported type - oh dear */ 
    ret=-1;
    errno=EBADF;
    break;
  }
  return ret;
}

size_t url_fread(void *ptr, size_t size, size_t nmemb, URL_FILE *file)
{
  size_t want;

  switch(file->type) {
  case CFTYPE_FILE:
    want=fread(ptr,size,nmemb,file->handle.file);
    break;

  case CFTYPE_CURL:
    want = nmemb * size;

    fill_buffer(file,want);

    /* check if theres data in the buffer - if not fill_buffer()
     * either errored or EOF */ 
    if(!file->buffer_pos)
      return 0;

    /* ensure only available data is considered */ 
    if(file->buffer_pos < want)
      want = file->buffer_pos;

    /* xfer data to caller */ 
    memcpy(ptr, file->buffer, want);

    use_buffer(file,want);

    want = want / size;     /* number of items */ 
    break;

  default: /* unknown or supported type - oh dear */ 
    want=0;
    errno=EBADF;
    break;

  }
  return want;
}

char *url_fgets(char *ptr, size_t size, URL_FILE *file)
{
  size_t want = size - 1;/* always need to leave room for zero termination */ 
  size_t loop;

  switch(file->type) {
  case CFTYPE_FILE:
    ptr = fgets(ptr,size,file->handle.file);
    break;

  case CFTYPE_CURL:
    fill_buffer(file,want);

    /* check if theres data in the buffer - if not fill either errored or
     * EOF */ 
    if(!file->buffer_pos)
      return NULL;

    /* ensure only available data is considered */ 
    if(file->buffer_pos < want)
      want = file->buffer_pos;

    /*buffer contains data */ 
    /* look for newline or eof */ 
    for(loop=0;loop < want;loop++) {
      if(file->buffer[loop] == '\n') {
        want=loop+1;/* include newline */ 
        break;
      }
    }

    /* xfer data to caller */ 
    memcpy(ptr, file->buffer, want);
    ptr[want]=0;/* allways null terminate */ 

    use_buffer(file,want);

    break;

  default: /* unknown or supported type - oh dear */ 
    ptr=NULL;
    errno=EBADF;
    break;
  }

  return ptr;/*success */ 
}

void url_rewind(URL_FILE *file)
{
  switch(file->type) {
  case CFTYPE_FILE:
    rewind(file->handle.file); /* passthrough */ 
    break;

  case CFTYPE_CURL:
    /* halt transaction */ 
    curl_multi_remove_handle(multi_handle, file->handle.curl);

    /* restart */ 
    curl_multi_add_handle(multi_handle, file->handle.curl);

    /* ditch buffer - write will recreate - resets stream pos*/ 
    if(file->buffer)
      free(file->buffer);

    file->buffer=NULL;
    file->buffer_pos=0;
    file->buffer_len=0;

    break;

  default: /* unknown or supported type - oh dear */ 
    break;
  }
}

/* Small main program to retrive from a url using fgets and fread saving the
 * output to two test files (note the fgets method will corrupt binary files if
 * they contain 0 chars */ 
int main(int argc, char *argv[])
{
  URL_FILE *handle;
  FILE *outf;

  int nread;
  char buffer[256];
  const char *url;

  if(argc < 2)
    url="http://192.168.7.3/testfile";/* default to testurl */ 
  else
    url=argv[1];/* use passed url */ 

  /* copy from url line by line with fgets */ 
  outf=fopen("fgets.test","w+");
  if(!outf) {
    perror("couldn't open fgets output file\n");
    return 1;
  }

  handle = url_fopen(url, "r");
  if(!handle) {
    printf("couldn't url_fopen() %s\n", url);
    fclose(outf);
    return 2;
  }

  while(!url_feof(handle)) {
    url_fgets(buffer,sizeof(buffer),handle);
    fwrite(buffer,1,strlen(buffer),outf);
  }

  url_fclose(handle);

  fclose(outf);


  /* Copy from url with fread */ 
  outf=fopen("fread.test","w+");
  if(!outf) {
    perror("couldn't open fread output file\n");
    return 1;
  }

  handle = url_fopen("testfile", "r");
  if(!handle) {
    printf("couldn't url_fopen() testfile\n");
    fclose(outf);
    return 2;
  }

  do {
    nread = url_fread(buffer, 1,sizeof(buffer), handle);
    fwrite(buffer,1,nread,outf);
  } while(nread);

  url_fclose(handle);

  fclose(outf);


  /* Test rewind */ 
  outf=fopen("rewind.test","w+");
  if(!outf) {
    perror("couldn't open fread output file\n");
    return 1;
  }

  handle = url_fopen("testfile", "r");
  if(!handle) {
    printf("couldn't url_fopen() testfile\n");
    fclose(outf);
    return 2;
  }

  nread = url_fread(buffer, 1,sizeof(buffer), handle);
  fwrite(buffer,1,nread,outf);
  url_rewind(handle);

  buffer[0]='\n';
  fwrite(buffer,1,1,outf);

  nread = url_fread(buffer, 1,sizeof(buffer), handle);
  fwrite(buffer,1,nread,outf);


  url_fclose(handle);

  fclose(outf);


  return 0;/* all done */ 
}
like image 41
Igor Avatar answered Sep 20 '22 04:09

Igor