コード例 #1
0
ファイル: response.c プロジェクト: MathieuClusiau/wonder
void resp_free(HTTPResponse *resp)
{
   if (resp->headers) {
      st_free(resp->headers);
   }
   if (resp->responseStrings)
      str_free(resp->responseStrings);
   if (resp->statusMsg && (resp->statusMsg != apple))
      WOFREE(resp->statusMsg);
   if (resp->content && !(resp->flags & RESP_DONT_FREE_CONTENT))
      WOFREE(resp->content);
   if (resp->instanceConnection && (resp->flags & RESP_CLOSE_CONNECTION))
      tr_close(resp->instanceConnection, resp->instHandle, resp->keepConnection);
   WOFREE(resp);
}
コード例 #2
0
ファイル: rtr.c プロジェクト: ColinBS/rtrlib
void rtr_stop(struct rtr_socket *rtr_socket)
{
    RTR_DBG1("rtr_stop()");
    rtr_change_socket_state(rtr_socket, RTR_SHUTDOWN);
    if(rtr_socket->thread_id != 0) {
        RTR_DBG1("pthread_cancel()");
        pthread_cancel(rtr_socket->thread_id);
        RTR_DBG1("pthread_join()");
        pthread_join(rtr_socket->thread_id, NULL);

        tr_close(rtr_socket->tr_socket);
        rtr_socket->request_session_id = true;
        rtr_socket->serial_number = 0;
        rtr_socket->last_update = 0;
        pfx_table_src_remove(rtr_socket->pfx_table, rtr_socket);
        spki_table_src_remove(rtr_socket->spki_table, rtr_socket);
        rtr_socket->thread_id = 0;
    }
    RTR_DBG1("Socket shut down");
}
コード例 #3
0
ファイル: tr.c プロジェクト: tenente-yk/bedmoni
void tr_reset(void)
{
  char fname[150];
  char fpath[150];
  char s[200];

  pthread_mutex_lock(&mutex);

  tr_close();

  snprintf(fpath, sizeof(fpath), "%s/%s", USBDISK_PATH, DATA_DIR);
  snprintf(fname, sizeof(fname), "%s", TR_DATA_FNAME);
  snprintf(s, sizeof(s), "%s/%s", fpath, fname);
  unlink(s);
  snprintf(fpath, sizeof(fpath), "%s/%s", USBDISK_PATH, DATA_DIR);
  snprintf(fname, sizeof(fname), "%s", TR_HEAD_FNAME);
  snprintf(s, sizeof(s), "%s/%s", fpath, fname);
  unlink(s);

  tr_open();

  pthread_mutex_unlock(&mutex);
}
コード例 #4
0
ファイル: libbt_api.c プロジェクト: alkap007/ali3606
int libbt_cleanup()
{
	if(engine)
	{
		//firstly close all torrent 
		int i;
		for(i = 0; i < MAX_DOWNLOAD_NS; i++)
		{
			if(g_tor_array[i])
			{
				tr_torrentClose(engine, g_tor_array[i]);
				g_tor_array[i] = NULL;
			}
					
		}
    	tr_close(engine);
		engine = NULL;
	}
	if(g_tor_array)
	{
		FREE(g_tor_array);
	}
	return 0;
}
コード例 #5
0
ファイル: transaction.c プロジェクト: ATLTed/wonder
/*
 *	run the request
 */
static HTTPResponse *_runRequest(WOAppReq *app, WOAppHandle woappHandle, WOInstanceHandle instHandle, HTTPRequest *req) {
   WOConnection *c;
   HTTPResponse *resp = NULL;
   int send_status, keepConnection, retryRequest = 0;
   const char *idString = NULL;

   WOLog(WO_INFO,"Trying to contact %s:%s on %s(%d)", app->name,app->instance,app->host,app->port);

   /* Tag the request with a unique identifier (if it hasn't been already) */
   /* (It might already have been tagged if we are retrying the request due to refusing new sessions, for example) */
   idString = req_HeaderForKey(req, REQUEST_ID_HEADER);
   if (idString == NULL)
   {
      String *requestID = tr_uniqueID();
      if (requestID)
      {
         req_addHeader(req, REQUEST_ID_HEADER, requestID->text, STR_COPYVALUE);
         idString = req_HeaderForKey(req, REQUEST_ID_HEADER);
         str_free(requestID);
      }
   }
   
   do {
      c = tr_open(instHandle);				/* connect */

      if (c == NULL) {
         WOLog(WO_INFO,"%s:%s NOT LISTENING on %s(%d)", app->name,app->instance,app->host,app->port);
         app->error = err_connect;
         return NULL;
      }

      /*
       *	app found and is listening on port
       */
      if (app->scheduler->beginTransaction)
         app->scheduler->beginTransaction(app, instHandle);

      /* Make sure that we're the only connection header, and we're explicit about the setting */
      req_removeHeader(req,CONNECTION);
      if (c->isPooled) {
         req_addHeader(req,CONNECTION,HTTP_KEEP_ALIVE,0);
      } else {
	 req_addHeader(req,CONNECTION,HTTP_CLOSE,0);
      }

      WOLog(WO_INFO,"%s:%s on %s(%d) connected [pooled: %s]", app->name, app->instance, app->host, app->port, c->isPooled ? "Yes" : "No");

      /*
       *	send the request....
       */
      send_status = req_sendRequest(req, c->fd);
      if (send_status != 0) {
         if ((send_status == TR_RESET) && (retryRequest == 0) && !req->haveReadStreamedData)  {
              /* If we get here the connection was reset. This means the instance has either quit or crashed. */
              /* Bump the generation number so all pooled connections to this instance will be invalidated. */
              /* Then retry the request with a new connection. If the instance is not running the retry will */
              /* fail with a different error and the instance will be marked dead. */
              _WOInstance *inst = ac_lockInstance(instHandle);
              /* note: if we get here, keepConnection == 0 --> this connection will be closed */
              if (inst)
              {
                  ac_cycleInstance(inst, c->generation);
                  ac_unlockInstance(instHandle);
              }
              retryRequest++;
              WOLog(WO_INFO, "retrying request due to connection reset");

              /* Must close connection before continuing */
              tr_close(c, instHandle, 0);
              continue;
          } else {
              WOLog(WO_ERR,"Failed to send request");
              tr_close(c, instHandle, 0);          /* close app connection */
              if (send_status == -1)
                 app->error = err_read;
              else
                 app->error = err_send;
              return NULL;
          }
      }

      /* Note that we have a request queued */
      WOLog(WO_INFO,"Request %s sent, awaiting response", req->request_str);

      /* While the app is processing the request, take the opportunity to check/update the config. */
      ac_readConfiguration();

      /*
       *	now wait for the response...
       */
      resp = resp_getResponseHeaders(c, instHandle);
      /* go ahead and read the first chunk of response data */
      if (resp && req->method != HTTP_HEAD_METHOD)
      {
         if (resp_getResponseContent(resp, 1) == -1)
         {
            resp_free(resp);
            resp = NULL;
         }
      }

      /* Validate the ID */
      if (idString && resp)
      {
         const char *respID = st_valueFor(resp->headers, REQUEST_ID_HEADER);
         if (respID != NULL)
         {
            if (strcmp(respID, idString) != 0)
            {
               WOLog(WO_ERR, "Got response with wrong ID! Dumping response. request ID = %s, response ID = %s", idString, respID);
               /* note this will cause the connection to be closed below */
               resp_free(resp);
               resp = NULL;
            } else
               st_removeKey(resp->headers, REQUEST_ID_HEADER);
         } else
            WOLog(WO_WARN, "Got response with no ID.");
      }
               
      app->response = resp;

      /*
       *	check if this connection can be kept open
       */
      keepConnection = 0;

#ifndef CGI /* doesn't make sense to keep the connection for CGI */
      if (resp && resp->headers)
      {
         const char *keepAlive;
         keepAlive = st_valueFor(resp->headers, CONNECTION);
         if (keepAlive)
         {
            /* if the keep alive header is set, honor the value */
            if (strcasecmp(keepAlive, HTTP_KEEP_ALIVE) == 0)
               keepConnection = 1;
         } else {
            /* no keep alive header - keep alive by default for HTTP/1.1 only */
            if (resp->flags & RESP_HTTP11)
               keepConnection = 1;
         }
      }
#endif

      if (resp != NULL) {
         if (app->scheduler->finalizeTransaction)
            if (app->scheduler->finalizeTransaction(app, instHandle))
               keepConnection = 0;

         st_removeKey(resp->headers, REFUSING_SESSIONS_HEADER);
         st_removeKey(resp->headers, LOAD_AVERAGE_HEADER);
         st_removeKey(resp->headers, CONNECTION);

         WOLog(WO_INFO,"received ->%d %s",resp->status,resp->statusMsg);
         retryRequest = 0;
      } else {
         if (c != NULL && tr_connectionWasReset(c))
         {
            /* If we get here the connection was reset. This means the instance has either quit or crashed. */
            /* Bump the generation number so all pooled connections to this instance will be invalidated. */
            /* Then retry the request with a new connection. If the instance is not running the retry will */
            /* fail with a different error and the instance will be marked dead. */
            /* Note that only one retry due to a connection reset error is allowed. This is to prevent an */
            /* infinite loop if the instance dies while processing the request and restarts quickly enough */
            /* to process the retry. */
            _WOInstance *inst = ac_lockInstance(instHandle);
            /* note: if we get here, keepConnection == 0 --> this connection will be closed */
            if (inst)
            {
               ac_cycleInstance(inst, c->generation);
               ac_unlockInstance(instHandle);
            }
            retryRequest++;
            if (retryRequest == 1)
               WOLog(WO_INFO, "retrying request due to connection reset");
         } else
         app->error = err_response;
      }
      if (resp && resp->content_read < resp->content_length)
      {
         resp->keepConnection = keepConnection;
         resp->instHandle = instHandle;
         resp->flags |= RESP_CLOSE_CONNECTION;
      } else {
         tr_close(c, instHandle, keepConnection);
      }
   } while (retryRequest == 1);

   return resp;
}
コード例 #6
0
ファイル: rtr.c プロジェクト: mceyran/rtrlib
void rtr_fsm_start(struct rtr_socket *rtr_socket) {
    rtr_socket->state = RTR_CONNECTING;
    install_sig_handler();
    while(1) {
        if(rtr_socket->state == RTR_CONNECTING) {
            RTR_DBG1("State: RTR_CONNECTING");
            //old pfx_record could exists in the pfx_table, check if they are too old and must be removed
            rtr_purge_outdated_records(rtr_socket);

            if(tr_open(rtr_socket->tr_socket) == TR_ERROR) {
                rtr_change_socket_state(rtr_socket, RTR_ERROR_TRANSPORT);
            }
            else if(rtr_socket->request_session_id) {
                //change to state RESET, if socket dont has a session_id
                rtr_change_socket_state(rtr_socket, RTR_RESET);
            }
            else {
                //if we already have a session_id, send a serial query and start to sync
                if(rtr_send_serial_query(rtr_socket) == RTR_SUCCESS)
                    rtr_change_socket_state(rtr_socket, RTR_SYNC);
                else
                    rtr_change_socket_state(rtr_socket, RTR_ERROR_FATAL);
            }

        }

        else if(rtr_socket->state == RTR_RESET) {
            RTR_DBG1("State: RTR_RESET");
            if (rtr_send_reset_query(rtr_socket) == 0) {
                RTR_DBG1("rtr_start: reset pdu sent");
                rtr_change_socket_state(rtr_socket, RTR_SYNC); //send reset query after connection established
            }
        }

        else if(rtr_socket->state == RTR_SYNC) {
            RTR_DBG1("State: RTR_SYNC");
            if(rtr_sync(rtr_socket) == 0)
                rtr_change_socket_state(rtr_socket, RTR_ESTABLISHED); //send reset query after connection established
        }

        else if(rtr_socket->state == RTR_ESTABLISHED) {
            RTR_DBG1("State: RTR_ESTABLISHED");
            if(rtr_wait_for_sync(rtr_socket) == RTR_SUCCESS) { //blocks till cache_timeout is expired or PDU was received
                //serial query senden
                if(rtr_send_serial_query(rtr_socket) == RTR_SUCCESS)
                    rtr_change_socket_state(rtr_socket, RTR_SYNC);
            }
        }

        else if(rtr_socket->state == RTR_ERROR_NO_DATA_AVAIL) {
            RTR_DBG1("State: RTR_ERROR_NO_DATA_AVAIL");
            rtr_socket->request_session_id = true;
            rtr_socket->serial_number = 0;
            rtr_change_socket_state(rtr_socket, RTR_RESET);
            sleep(ERR_TIMEOUT);
            rtr_purge_outdated_records(rtr_socket);
        }

        else if(rtr_socket->state == RTR_ERROR_NO_INCR_UPDATE_AVAIL) {
            RTR_DBG1("State: RTR_ERROR_NO_INCR_UPDATE_AVAIL");
            rtr_socket->request_session_id = true;
            rtr_socket->serial_number = 0;
            rtr_change_socket_state(rtr_socket, RTR_RESET);
            rtr_purge_outdated_records(rtr_socket);
        }

        else if(rtr_socket->state == RTR_ERROR_TRANSPORT) {
            RTR_DBG1("State: RTR_ERROR_TRANSPORT");
            tr_close(rtr_socket->tr_socket);
            rtr_change_socket_state(rtr_socket, RTR_CONNECTING);
            sleep(ERR_TIMEOUT);
        }

        else if(rtr_socket->state == RTR_ERROR_FATAL) {
            RTR_DBG1("State: RTR_ERROR_FATAL");
            tr_close(rtr_socket->tr_socket);
            rtr_change_socket_state(rtr_socket, RTR_CONNECTING);
            sleep(ERR_TIMEOUT);
        }

        else if(rtr_socket->state == RTR_SHUTDOWN) {
            RTR_DBG1("State: RTR_SHUTDOWN");
            tr_close(rtr_socket->tr_socket);
            rtr_socket->request_session_id = true;
            rtr_socket->serial_number = 0;
            rtr_socket->last_update = 0;
            pfx_table_src_remove(rtr_socket->pfx_table, rtr_socket);
            pthread_exit(NULL);
        }
    }
}
コード例 #7
0
ファイル: rtr.c プロジェクト: ColinBS/rtrlib
/* WARNING: This Function has cancelable sections*/
void *rtr_fsm_start(struct rtr_socket *rtr_socket)
{
   if (rtr_socket->state == RTR_SHUTDOWN)
	return NULL;

    // We don't care about the old state, but POSIX demands a non null value for setcancelstate
    int oldcancelstate;
    pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldcancelstate);

    rtr_socket->state = RTR_CONNECTING;
    while(1) {
        if(rtr_socket->state == RTR_CONNECTING) {
            RTR_DBG1("State: RTR_CONNECTING");
            rtr_socket->has_received_pdus = false;

            //old pfx_record could exists in the pfx_table, check if they are too old and must be removed
            //old key_entry could exists in the spki_table, check if they are too old and must be removed
            rtr_purge_outdated_records(rtr_socket);

            if(tr_open(rtr_socket->tr_socket) == TR_ERROR) {
                rtr_change_socket_state(rtr_socket, RTR_ERROR_TRANSPORT);
            } else if(rtr_socket->request_session_id) {
                //change to state RESET, if socket dont has a session_id
                rtr_change_socket_state(rtr_socket, RTR_RESET);
            } else {
                //if we already have a session_id, send a serial query and start to sync
                if(rtr_send_serial_query(rtr_socket) == RTR_SUCCESS)
                    rtr_change_socket_state(rtr_socket, RTR_SYNC);
                else
                    rtr_change_socket_state(rtr_socket, RTR_ERROR_FATAL);
            }

        }

        else if(rtr_socket->state == RTR_RESET) {
            RTR_DBG1("State: RTR_RESET");
            if (rtr_send_reset_query(rtr_socket) == RTR_SUCCESS) {
                RTR_DBG1("rtr_start: reset pdu sent");
                rtr_change_socket_state(rtr_socket, RTR_SYNC); //start to sync after connection is established
            }
        }

        else if(rtr_socket->state == RTR_SYNC) {
            RTR_DBG1("State: RTR_SYNC");
            if(rtr_sync(rtr_socket) == RTR_SUCCESS)
                rtr_change_socket_state(rtr_socket, RTR_ESTABLISHED); //wait for next sync after first successful sync
        }

        else if(rtr_socket->state == RTR_ESTABLISHED) {
            RTR_DBG1("State: RTR_ESTABLISHED");

            // Allow thread cancellation for recv code path only.
            // This should be enough since we spend most of the time blocking on recv
            pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &oldcancelstate);
            int ret = rtr_wait_for_sync(rtr_socket); //blocks till expire_interval is expired or PDU was received
            pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldcancelstate);

            if(ret == RTR_SUCCESS) { //send serial query
                if(rtr_send_serial_query(rtr_socket) == RTR_SUCCESS)
                    rtr_change_socket_state(rtr_socket, RTR_SYNC);
            }
        }

        else if(rtr_socket->state == RTR_FAST_RECONNECT){
            RTR_DBG1("State: RTR_FAST_RECONNECT");
            tr_close(rtr_socket->tr_socket);
            rtr_change_socket_state(rtr_socket, RTR_CONNECTING);
        }

        else if(rtr_socket->state == RTR_ERROR_NO_DATA_AVAIL) {
            RTR_DBG1("State: RTR_ERROR_NO_DATA_AVAIL");
            rtr_socket->request_session_id = true;
            rtr_socket->serial_number = 0;
            rtr_change_socket_state(rtr_socket, RTR_RESET);
            sleep(rtr_socket->retry_interval);
            rtr_purge_outdated_records(rtr_socket);
        }

        else if(rtr_socket->state == RTR_ERROR_NO_INCR_UPDATE_AVAIL) {
            RTR_DBG1("State: RTR_ERROR_NO_INCR_UPDATE_AVAIL");
            rtr_socket->request_session_id = true;
            rtr_socket->serial_number = 0;
            rtr_change_socket_state(rtr_socket, RTR_RESET);
            rtr_purge_outdated_records(rtr_socket);
        }

        else if(rtr_socket->state == RTR_ERROR_TRANSPORT) {
            RTR_DBG1("State: RTR_ERROR_TRANSPORT");
            tr_close(rtr_socket->tr_socket);
            rtr_change_socket_state(rtr_socket, RTR_CONNECTING);
            RTR_DBG("Waiting %u", rtr_socket->retry_interval);
            sleep(rtr_socket->retry_interval);
        }

        else if(rtr_socket->state == RTR_ERROR_FATAL) {
            RTR_DBG1("State: RTR_ERROR_FATAL");
            tr_close(rtr_socket->tr_socket);
            rtr_change_socket_state(rtr_socket, RTR_CONNECTING);
            RTR_DBG("Waiting %u", rtr_socket->retry_interval);
            sleep(rtr_socket->retry_interval);
        }

        else if(rtr_socket->state == RTR_SHUTDOWN) {
            RTR_DBG1("State: RTR_SHUTDOWN");
            pthread_exit(NULL);
        }
    }
}
コード例 #8
0
/*******************************************************************************
 * main
 ******************************************************************************/
int main(int argc, char ** argv) {

	/* vars */
	int i, error, nat;
	tr_handle_t * h;
	tr_torrent_t * tor;
	tr_stat_t * s;
	double tf_sharing = 0.0;
	char tf_string[80];
	int tf_seeders, tf_leechers;

	/* Get options */
	if (parseCommandLine(argc, argv)) {
		printf("Transmission %s [%d] - tfCLI [%d]\nhttp://transmission.m0k.org/ - http://tf-b4rt.berlios.de/\n\n",
			VERSION_STRING, VERSION_REVISION, VERSION_REVISION_CLI);
		printf(USAGE, argv[0], TR_DEFAULT_PORT);
		return 1;
	}

	/* show help */
	if (showHelp) {
		printf("Transmission %s [%d] - tfCLI [%d]\nhttp://transmission.m0k.org/ - http://tf-b4rt.berlios.de/\n\n",
			VERSION_STRING, VERSION_REVISION, VERSION_REVISION_CLI);
		printf(USAGE, argv[0], TR_DEFAULT_PORT);
		return 0;
	}

	// verbose
	if (verboseLevel < 0) {
		verboseLevel = 0;
	} else if (verboseLevel > 9) {
		verboseLevel = 9;
	}
	if (verboseLevel) {
		static char env[11];
		sprintf(env, "TR_DEBUG=%d", verboseLevel);
		putenv(env);
	}

	// check port
	if (bindPort < 1 || bindPort > 65535) {
		printf("Invalid port '%d'\n", bindPort);
		return 1;
	}

	// Initialize libtransmission
	h = tr_init();

	// Open and parse torrent file
	if (!(tor = tr_torrentInit(h, torrentPath, 0, &error))) {
		printf("Failed opening torrent file `%s'\n", torrentPath);
		goto failed;
	}

	/* show info */
	if (showInfo) {
		// info
		tr_info_t * info = tr_torrentInfo(tor);
		// stat
		s = tr_torrentStat(tor);
		// Print torrent info (quite à la btshowmetainfo)
		printf("hash:     ");
		for (i = 0; i < SHA_DIGEST_LENGTH; i++) {
			printf("%02x", info->hash[i]);
		}
		printf("\n");
        printf( "tracker:  %s:%d\n",
                s->trackerAddress, s->trackerPort );
        printf( "announce: %s\n", s->trackerAnnounce );
		printf("size:     %"PRIu64" (%"PRIu64" * %d + %"PRIu64")\n",
			info->totalSize, info->totalSize / info->pieceSize,
			info->pieceSize, info->totalSize % info->pieceSize);
		if (info->comment[0]) {
			printf("comment:  %s\n", info->comment);
		}
		if (info->creator[0]) {
			printf("creator:  %s\n", info->creator);
		}
		printf("file(s):\n");
		for (i = 0; i < info->fileCount; i++) {
			printf(" %s (%"PRIu64")\n", info->files[i].name,
				info->files[i].length);
		}
		// cleanup
		goto cleanup;
	}

	/* show scrape */
	if (showScrape) {
		int seeders, leechers, downloaded;
		if (tr_torrentScrape(tor, &seeders, &leechers, &downloaded)) {
			printf("Scrape failed.\n");
		} else {
			printf("%d seeder(s), %d leecher(s), %d download(s).\n",
					seeders, leechers, downloaded);
		}
		// cleanup
		goto cleanup;
	}

	/* start up transmission */

	// Create PID file if wanted by user
	if (tf_pid != NULL) {
		FILE * pid_file;
		pid_file = fopen(tf_pid, "w+");
		if (pid_file != NULL) {
			fprintf(pid_file, "%d", getpid());
			fclose(pid_file);
		}
	}

	// signal
	signal(SIGINT, sigHandler);

	// If running torrentflux, Download limit = 0 means no limit
	if (tf_stat_file != NULL) { /* tfCLI */
		// up
		switch (uploadLimit) {
			case 0:
				uploadLimit = -1;
				break;
			case -2:
				uploadLimit = 0;
				break;
		}
		// down
		switch (downloadLimit) {
			case 0:
				downloadLimit = -1;
				break;
			case -2:
				downloadLimit = 0;
				break;
		}
	}

	// init some things
	tr_setBindPort(h, bindPort);
	tr_setGlobalUploadLimit(h, uploadLimit);
	tr_setGlobalDownloadLimit(h, downloadLimit);

	// nat-traversal
	if (natTraversal) {
		tr_natTraversalEnable(h);
	} else {
		tr_natTraversalDisable(h);
	}

	// set folder
	tr_torrentSetFolder(tor, ".");

	// start the torrent
	tr_torrentStart(tor);

	/* main-loop */
	while (!mustDie) {

		// status-string
		char string[80];
		int chars = 0;
		//
		int result;
		char stat_state;

		// sleep
		sleep(displayInterval);

		// Check if we must stop
		if (tf_stat_file != NULL) { /* tfCLI */
			tf_stat = fopen(tf_stat_file, "r");
			if (tf_stat != NULL) {
				// Get state
				stat_state = fgetc(tf_stat);
				// Close the file
				fclose(tf_stat);
				// Torrentflux asked to shutdown the torrent
				if (stat_state == '0') {
					mustDie = 1;
				}
			}
		}

		// torrent-stat
		s = tr_torrentStat(tor);

		if (s->status & TR_STATUS_CHECK) { /* --- CHECK --- */

			if (tf_stat_file == NULL) { /* standalone */

				// status-string
				chars = snprintf(string, 80,
					"Checking files... %.2f %%", 100.0 * s->progress );

			} else { /* tfCLI */

				// write tf-stat-file
				tr_info_t * info = tr_torrentInfo(tor);
				tf_stat = fopen(tf_stat_file, "w+");
				if (tf_stat != NULL) {
					fprintf(tf_stat, "%d\n%.1f\n%s\n0 kB/s\n0 kB/s\n%s\n0\n0\n0.0\n%d\n0\n%" PRIu64 "\n%" PRIu64,
						1,                          /* State             */
						100.0 * s->progress,        /* checking progress */
						"Checking existing data",   /* State text        */
						                            /* download speed    */
						                            /* upload speed      */
						tf_user,                    /* user              */
						                            /* seeds             */
						                            /* peers             */
						                            /* sharing           */
						seedLimit,                  /* seedlimit         */
						                            /* uploaded bytes    */
						s->downloaded,              /* downloaded bytes  */
						info->totalSize);           /* global size       */
					fclose(tf_stat);
				}

			}

		} else if (s->status & TR_STATUS_DOWNLOAD) { /* --- DOWNLOAD --- */

			if (tf_stat_file == NULL) { /* standalone */

				// status-string
				chars = snprintf(string, 80,
					"Progress: %.2f %%, %d peer%s, dl from %d (%.2f KB/s), "
					"ul to %d (%.2f KB/s)", 100.0 * s->progress,
					s->peersTotal, ( s->peersTotal == 1 ) ? "" : "s",
					s->peersUploading, s->rateDownload,
					s->peersDownloading, s->rateUpload);

			} else { /* tfCLI */

				// sharing
				if (s->downloaded != 0) {
					tf_sharing =
						((double)(s->uploaded) / (double)(s->downloaded)) * 100;
				}

				// seeders + leechers
				if (s->seeders < 0) {
					tf_seeders = 0;
				} else {
					tf_seeders = s->seeders;
				}
				if (s->leechers < 0) {
					tf_leechers = 0;
				} else {
					tf_leechers = s->leechers;
				}

				// eta
				if (s->eta != -1) {
					// sanity-check. value of eta >= 7 days is not really of use
					if (s->eta < 604800) {
						if ((s->eta / (24 * 60 * 60)) != 0) {
							sprintf(tf_string,"%d:%02d:%02d:%02d",
								s->eta / (24 * 60 * 60),
								((s->eta) % (24 * 60 * 60)) / (60 * 60),
								((s->eta) % (60 * 60) / 60),
								s->eta % 60);
						} else if ((s->eta / (60 * 60)) != 0) {
							sprintf(tf_string, "%d:%02d:%02d",
								(s->eta) / (60 * 60),
								((s->eta) % (60 * 60) / 60),
								s->eta % 60);
						} else {
							sprintf(tf_string, "%d:%02d",
								(s->eta) / 60, s->eta % 60);
						}
					} else {
						sprintf(tf_string,"-");
					}
				} else {
					sprintf(tf_string,"-");
				}
				if ((s->seeders == -1) && (s->peersTotal == 0)) {
					sprintf(tf_string,"Connecting to Peers");
				}

				// write tf-stat-file
				tf_stat = fopen(tf_stat_file, "w+");
				if (tf_stat != NULL) {
					tr_info_t * info = tr_torrentInfo( tor );
					fprintf(tf_stat, "%d\n%.1f\n%s\n%.1f kB/s\n%.1f kB/s\n%s\n%d (%d)\n%d (%d)\n%.1f\n%d\n%" PRIu64 "\n%" PRIu64 "\n%" PRIu64,
						1,                                /* State            */
						100.0 * s->progress,              /* progress         */
						tf_string,                        /* Estimated time   */
						s->rateDownload,                  /* download speed   */
						s->rateUpload,                    /* upload speed     */
						tf_user,                          /* user             */
						s->peersUploading, tf_seeders,    /* seeds            */
						s->peersDownloading, tf_leechers, /* peers            */
						tf_sharing,                       /* sharing          */
						seedLimit,                        /* seedlimit        */
						s->uploaded,                      /* uploaded bytes   */
						s->downloaded,                    /* downloaded bytes */
						info->totalSize);                 /* global size      */
					fclose(tf_stat);
				}
			}

		} else if (s->status & TR_STATUS_SEED) { /* --- SEED --- */

			// info
			tr_info_t * info = tr_torrentInfo(tor);

			if (tf_stat_file == NULL) { /* standalone */

				// status-string
				chars = snprintf(string, 80,
					"Seeding, uploading to %d of %d peer(s), %.2f KB/s",
					s->peersDownloading, s->peersTotal,
					s->rateUpload);

			} else { /* tfCLI */

				// sharing
				if (s->downloaded != 0) {
					tf_sharing =
						((double)(s->uploaded) /
							(double)(s->downloaded)) * 100;
				} else {
					tf_sharing =
						((double)(s->uploaded) /
							(double)(info->totalSize)) * 100;
				}

				// If we reached the seeding limit, we have to quit transmission
				if ((seedLimit != 0) &&
					((tf_sharing > (double)(seedLimit)) ||
					(seedLimit == -1))) {
					mustDie = 1;
				}

				// seeders + leechers
				if (s->seeders < 0) {
					tf_seeders = 0;
				} else {
					tf_seeders = s->seeders;
				}
				if (s->leechers < 0) {
					tf_leechers = 0;
				} else {
					tf_leechers = s->leechers;
				}

				// write tf-stat-file
				tf_stat = fopen(tf_stat_file, "w+");
				if (tf_stat != NULL) {
					fprintf(tf_stat, "%d\n%.1f\n%s\n%.1f kB/s\n%.1f kB/s\n%s\n%d (%d)\n%d (%d)\n%.1f\n%d\n%" PRIu64 "\n%" PRIu64 "\n%" PRIu64,
						1,                                /* State            */
						100.0 * s->progress,              /* progress         */
						"Download Succeeded!",            /* State text       */
						s->rateDownload,                  /* download speed   */
						s->rateUpload,                    /* upload speed     */
						tf_user,                          /* user             */
						s->peersUploading, tf_seeders,    /* seeds            */
						s->peersDownloading, tf_leechers, /* peers            */
						tf_sharing,                       /* sharing          */
						seedLimit,                        /* seedlimit        */
						s->uploaded,                      /* uploaded bytes   */
						s->downloaded,                    /* downloaded bytes */
						info->totalSize);                 /* global size      */
					fclose(tf_stat);
				}
			}

		}

		// status-string
		if (tf_stat_file == NULL) { /* standalone */
			memset( &string[chars], ' ', 79 - chars );
			string[79] = '\0';
			// print status to stderr
			fprintf(stderr, "\r%s", string);
		}

		// errors
		if (s->error & TR_ETRACKER) {
			if (tf_stat_file == NULL) { /* standalone */
				// print errors to stderr
				fprintf(stderr, "\n%s\n", s->trackerError);
			} else { /* tfCLI */
				// append errors to stat-file
				tf_stat = fopen(tf_stat_file, "a+");
				if (tf_stat != NULL) {
					fprintf(tf_stat, "\n%s\n", s->trackerError);
					fclose(tf_stat);
				}
			}
		} else if (verboseLevel > 0) {
			if (tf_stat_file == NULL) { /* standalone */
				// stderr
				fprintf(stderr, "\n");
			}
		}

		// finishCall
		if (tr_getFinished(tor)) {
			result = system(finishCall);
		}

	} /* main-loop */

	// mark torrent as stopped in tf-stat-file
	if (tf_stat_file != NULL) { /* tfCLI */

		// info
		tr_info_t * info = tr_torrentInfo(tor);

		// sharing
		if (s->downloaded != 0) {
			tf_sharing =
				((double)(s->uploaded) / (double)(s->downloaded)) * 100;
		} else {
			tf_sharing =
				((double)(s->uploaded) / (double)(info->totalSize)) * 100;
		}

		// write tf-stat-file
		tf_stat = fopen(tf_stat_file, "w+");
		if (tf_stat != NULL) {
			float progress;
			if (s->status & TR_STATUS_SEED) {
				sprintf(tf_string,"Download Succeeded!");
				progress = 100;
			} else {
				sprintf(tf_string,"Torrent Stopped");
				progress = -(1 + s->progress) * 100;
			}
			fprintf(tf_stat, "%d\n%.1f\n%s\n\n\n%s\n\n\n%.1f\n%d\n%" PRIu64 "\n%" PRIu64 "\n%" PRIu64,
				0,                          /* State            */
				progress,                   /* progress         */
				tf_string,                  /* State text       */
				                            /* download speed   */
				                            /* upload speed     */
				tf_user,                    /* user             */
				                            /* seeds            */
				                            /* peers            */
				tf_sharing,                 /* sharing          */
				seedLimit,                  /* seedlimit        */
				s->uploaded,                /* uploaded bytes   */
				s->downloaded,              /* downloaded bytes */
				info->totalSize);           /* global size      */
			fclose(tf_stat);
		}
	}

	// stderr
	if (tf_stat_file == NULL) { /* standalone */
		fprintf(stderr, "\n");
	}

	// Try for 5 seconds to notify the tracker that we are leaving
	// and to delete any port mappings for nat traversal
	tr_torrentStop(tor);
	tr_natTraversalDisable(h);
	for (i = 0; i < 10; i++) {
		s = tr_torrentStat(tor);
		nat = tr_natTraversalStatus(h);
		if (s->status & TR_STATUS_PAUSE && TR_NAT_TRAVERSAL_DISABLED == nat) {
			// The 'stopped' tracker message was sent
			// and port mappings were deleted
			break;
		}
		usleep(500000);
	}

	// Remove PID file if created !
	if (tf_pid != NULL) {
		remove(tf_pid);
	}

cleanup:
	tr_torrentClose(h, tor);

failed:
	tr_close(h);

	return 0;
}