476 lines
14 KiB
C
476 lines
14 KiB
C
/*
|
|
Copyright (c) 2013-2014 Anton Titov.
|
|
|
|
Copyright (c) 2013-2014 pCloud Ltd. All rights reserved.
|
|
|
|
Redistribution and use in source and binary forms, with or without
|
|
modification, are permitted provided that the following conditions are
|
|
met: Redistributions of source code must retain the above copyright
|
|
notice, this list of conditions and the following disclaimer.
|
|
Redistributions in binary form must reproduce the above copyright
|
|
notice, this list of conditions and the following disclaimer in the
|
|
documentation and/or other materials provided with the distribution.
|
|
Neither the name of pCloud Ltd nor the names of its contributors may
|
|
be used to endorse or promote products derived from this software
|
|
without specific prior written permission.
|
|
|
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL pCloud Ltd BE
|
|
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
|
|
BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
|
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
|
|
OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
|
|
IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
#include <string.h>
|
|
#include "pcallbacks.h"
|
|
#include "pcompat.h"
|
|
#include "plibs.h"
|
|
#include "plist.h"
|
|
#include "pfolder.h"
|
|
#include "prunratelimit.h"
|
|
|
|
#define MAX_STATUS_STR_LEN 64
|
|
#define DONT_SHOW_TIME_IF_SEC_OVER (2*86400)
|
|
#define DONT_SHOW_TIME_IF_SPEED_BELOW (4*1024)
|
|
|
|
static pthread_mutex_t statusmutex=PTHREAD_MUTEX_INITIALIZER;
|
|
static pthread_cond_t statuscond=PTHREAD_COND_INITIALIZER;
|
|
static int statuschanges=0;
|
|
static int statusthreadrunning=0;
|
|
|
|
static pthread_mutex_t eventmutex=PTHREAD_MUTEX_INITIALIZER;
|
|
static pthread_cond_t eventcond=PTHREAD_COND_INITIALIZER;
|
|
static psync_list eventlist;
|
|
static int eventthreadrunning=0;
|
|
static pstatus_t status_old={ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
|
|
|
|
typedef struct {
|
|
psync_list list;
|
|
psync_eventdata_t data;
|
|
psync_eventtype_t event;
|
|
int freedata;
|
|
} event_list_t;
|
|
|
|
static char *cat_lstr(char *src, const char *app, size_t len){
|
|
return (char *)memcpy(src, app, len)+len;
|
|
}
|
|
|
|
static char *cat_str(char *src, const char *app){
|
|
return cat_lstr(src, app, strlen(app));
|
|
}
|
|
|
|
PSYNC_NOINLINE static char *cat_uint32l(char *src, uint32_t num){
|
|
char str[16];
|
|
uint32_t rem;
|
|
int unsigned len;
|
|
len=0;
|
|
do{
|
|
rem=num%10;
|
|
str[sizeof(str)-++len]='0'+rem;
|
|
} while (num/=10);
|
|
memcpy(src, str+sizeof(str)-len, len);
|
|
return src+len;
|
|
}
|
|
|
|
static char *cat_uint32(char *src, uint32_t num){
|
|
if (num>=1000)
|
|
return cat_uint32l(src, num);
|
|
if (num<100){
|
|
if (num<10)
|
|
goto format10;
|
|
else
|
|
goto format100;
|
|
}
|
|
*src++='0'+num/100;
|
|
num%=100;
|
|
format100:
|
|
*src++='0'+num/10;
|
|
num%=10;
|
|
format10:
|
|
*src++='0'+num;
|
|
return src;
|
|
}
|
|
|
|
#define cat_const(src, app) cat_lstr(src, app, sizeof(app)-1)
|
|
|
|
static char *fill_formatted_bytes(char *str, uint64_t bytes){
|
|
const static char *sizes[]={"Bytes", "KB", "MB", "GB", "TB"};
|
|
uint32_t rem, sz;
|
|
rem=0;
|
|
sz=0;
|
|
while (bytes>=1024 && sz<ARRAY_SIZE(sizes)-1){
|
|
rem=bytes%1024;
|
|
bytes/=1024;
|
|
sz++;
|
|
}
|
|
str=cat_uint32(str, (uint32_t)bytes);
|
|
if (sz && bytes<100){
|
|
rem=rem*1000/1024;
|
|
assert(rem<=999);
|
|
*str++='.';
|
|
if (bytes<10){
|
|
rem/=10;
|
|
*str++='0'+rem/10;
|
|
*str++='0'+rem%10;
|
|
}
|
|
else
|
|
*str++='0'+rem/100;
|
|
}
|
|
return cat_str(str, sizes[sz]);
|
|
}
|
|
|
|
static char *fill_remaining(char *str, uint32_t files, uint64_t bytes){
|
|
str=cat_const(str, "Remaining: ");
|
|
str=cat_uint32(str, files);
|
|
str=cat_const(str, " files, ");
|
|
return fill_formatted_bytes(str, bytes);
|
|
}
|
|
|
|
static char *fill_formatted_time(char *str, uint64_t totalsec){
|
|
uint32_t d, h, m, s;
|
|
s=totalsec%60;
|
|
totalsec/=60;
|
|
m=totalsec%60;
|
|
totalsec/=60;
|
|
h=totalsec%24;
|
|
d=totalsec/24;
|
|
if (d){
|
|
str=cat_uint32(str, d);
|
|
*str++='d';
|
|
*str++=' ';
|
|
if (m>=30)
|
|
h++;
|
|
str=cat_uint32(str, h);
|
|
*str++='h';
|
|
}
|
|
else if (h){
|
|
str=cat_uint32(str, h);
|
|
*str++='h';
|
|
*str++=' ';
|
|
if (s>=30)
|
|
m++;
|
|
str=cat_uint32(str, m);
|
|
*str++='m';
|
|
}
|
|
else if (m){
|
|
str=cat_uint32(str, m);
|
|
*str++='m';
|
|
*str++=' ';
|
|
str=cat_uint32(str, s);
|
|
*str++='s';
|
|
}
|
|
else {
|
|
if (!s)
|
|
s=1;
|
|
str=cat_uint32(str, s);
|
|
*str++='s';
|
|
}
|
|
return str;
|
|
}
|
|
|
|
static uint64_t sub_no_underf(uint64_t a, uint64_t b){
|
|
if (likely(b<a))
|
|
return a-b;
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
static void status_fill_formatted_str(pstatus_t *status, char *downloadstr, char *uploadstr){
|
|
char *up, *dw;
|
|
uint64_t remsec;
|
|
uint32_t speed;
|
|
dw=downloadstr;
|
|
up=uploadstr;
|
|
|
|
if (status->filestodownload){
|
|
speed=status->downloadspeed;
|
|
if (status->status==PSTATUS_PAUSED || status->status==PSTATUS_STOPPED || status->localisfull || speed==0){
|
|
if (status->status==PSTATUS_PAUSED)
|
|
dw=cat_const(dw, "Paused. ");
|
|
else if (status->status==PSTATUS_STOPPED)
|
|
dw=cat_const(dw, "Stopped. ");
|
|
else if (status->localisfull)
|
|
dw=cat_const(dw, "Disk full. ");
|
|
dw=fill_remaining(dw, status->filestodownload, sub_no_underf(status->bytestodownload, status->bytesdownloaded));
|
|
}
|
|
else{
|
|
dw=fill_formatted_bytes(dw, speed);
|
|
dw=cat_const(dw, "/sec, ");
|
|
dw=fill_remaining(dw, status->filestodownload, sub_no_underf(status->bytestodownload, status->bytesdownloaded));
|
|
remsec=(status->bytestodownload-status->bytesdownloaded)/speed;
|
|
if (remsec<DONT_SHOW_TIME_IF_SEC_OVER || speed>=DONT_SHOW_TIME_IF_SPEED_BELOW){
|
|
*dw++=' ';
|
|
dw=fill_formatted_time(dw, remsec);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
dw=cat_const(dw, "Everything Downloaded");
|
|
|
|
if (status->filestoupload){
|
|
speed=status->uploadspeed;
|
|
if (status->status==PSTATUS_PAUSED || status->status==PSTATUS_STOPPED || status->remoteisfull || speed==0){
|
|
if (status->status==PSTATUS_PAUSED)
|
|
up=cat_const(up, "Paused. ");
|
|
else if (status->status==PSTATUS_STOPPED)
|
|
up=cat_const(up, "Stopped. ");
|
|
else if (status->remoteisfull)
|
|
up=cat_const(up, "Account full. ");
|
|
up=fill_remaining(up, status->filestoupload, sub_no_underf(status->bytestoupload, status->bytesuploaded));
|
|
}
|
|
else{
|
|
up=fill_formatted_bytes(up, speed);
|
|
up=cat_const(up, "/sec, ");
|
|
up=fill_remaining(up, status->filestoupload, sub_no_underf(status->bytestoupload, status->bytesuploaded));
|
|
remsec=(status->bytestoupload-status->bytesuploaded)/speed;
|
|
if (remsec<DONT_SHOW_TIME_IF_SEC_OVER || speed>=DONT_SHOW_TIME_IF_SPEED_BELOW){
|
|
*up++=' ';
|
|
up=fill_formatted_time(up, remsec);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
up=cat_const(up, "Everything Uploaded");
|
|
|
|
assert(dw<downloadstr+MAX_STATUS_STR_LEN);
|
|
assert(up<uploadstr+MAX_STATUS_STR_LEN);
|
|
*dw=0;
|
|
*up=0;
|
|
status->downloadstr=downloadstr;
|
|
status->uploadstr=uploadstr;
|
|
}
|
|
|
|
void psync_callbacks_get_status(pstatus_t *status){
|
|
static char downloadstr[MAX_STATUS_STR_LEN], uploadstr[MAX_STATUS_STR_LEN];
|
|
memcpy(status, &psync_status, sizeof(pstatus_t));
|
|
status_fill_formatted_str(status, downloadstr, uploadstr);
|
|
}
|
|
|
|
static void status_change_thread(void *ptr){
|
|
char downloadstr[MAX_STATUS_STR_LEN], uploadstr[MAX_STATUS_STR_LEN];
|
|
pstatus_change_callback_t callback=(pstatus_change_callback_t)ptr;
|
|
while (1){
|
|
// Maximum 2 updates/sec
|
|
psync_milisleep(500);
|
|
pthread_mutex_lock(&statusmutex);
|
|
while (statuschanges<=0){
|
|
statuschanges=-1;
|
|
pthread_cond_wait(&statuscond, &statusmutex);
|
|
}
|
|
statuschanges=0;
|
|
if (((status_old.filestodownload > 0 ) && (psync_status.filestodownload == 0)) ||
|
|
((psync_status.filestodownload > 0 ) && (status_old.filestodownload == 0)) ||
|
|
((status_old.filestoupload > 0 ) && (psync_status.filestoupload == 0)) ||
|
|
((psync_status.filestoupload > 0 ) && (status_old.filestoupload == 0)) ||
|
|
((psync_status.localisfull != status_old.localisfull)) ||
|
|
((psync_status.remoteisfull != status_old.remoteisfull)) ||
|
|
((psync_status.status != status_old.status) && (
|
|
(psync_status.status == PSTATUS_STOPPED) ||
|
|
(psync_status.status == PSTATUS_PAUSED) ||
|
|
(psync_status.status == PSTATUS_OFFLINE) ||
|
|
(status_old.status == PSTATUS_STOPPED) ||
|
|
(status_old.status == PSTATUS_PAUSED) ||
|
|
(status_old.status == PSTATUS_OFFLINE) ) )
|
|
)
|
|
psync_run_ratelimited("rebuild icons", psync_rebuild_icons, 1, 1);
|
|
status_old = psync_status;
|
|
pthread_mutex_unlock(&statusmutex);
|
|
if (!psync_do_run)
|
|
break;
|
|
status_fill_formatted_str(&psync_status, downloadstr, uploadstr);
|
|
debug(D_NOTICE, "sending status update, dwlstr: %s, uplstr: %s", psync_status.downloadstr, psync_status.uploadstr);
|
|
callback(&psync_status);
|
|
}
|
|
}
|
|
|
|
void psync_set_status_callback(pstatus_change_callback_t callback){
|
|
pthread_mutex_lock(&statusmutex);
|
|
statusthreadrunning=1;
|
|
pthread_mutex_unlock(&statusmutex);
|
|
psync_run_thread1("status change", status_change_thread, callback);
|
|
}
|
|
|
|
void psync_send_status_update(){
|
|
if (statusthreadrunning){
|
|
pthread_mutex_lock(&statusmutex);
|
|
if (++statuschanges==0){
|
|
statuschanges++;
|
|
pthread_cond_signal(&statuscond);
|
|
}
|
|
pthread_mutex_unlock(&statusmutex);
|
|
}
|
|
}
|
|
|
|
static void event_thread(void *ptr){
|
|
pevent_callback_t callback=(pevent_callback_t)ptr;
|
|
event_list_t *event;
|
|
|
|
while (1){
|
|
pthread_mutex_lock(&eventmutex);
|
|
|
|
while (psync_list_isempty(&eventlist))
|
|
pthread_cond_wait(&eventcond, &eventmutex);
|
|
|
|
event=psync_list_remove_head_element(&eventlist, event_list_t, list);
|
|
|
|
pthread_mutex_unlock(&eventmutex);
|
|
|
|
if (!psync_do_run)
|
|
break;
|
|
|
|
callback(event->event, event->data);
|
|
|
|
if (event->freedata)
|
|
psync_free(event->data.ptr);
|
|
|
|
psync_free(event);
|
|
}
|
|
}
|
|
|
|
void psync_set_event_callback(pevent_callback_t callback){
|
|
pthread_mutex_lock(&statusmutex);
|
|
eventthreadrunning=1;
|
|
pthread_mutex_unlock(&statusmutex);
|
|
psync_list_init(&eventlist);
|
|
psync_run_thread1("event", event_thread, callback);
|
|
}
|
|
|
|
void psync_send_event_by_id(psync_eventtype_t eventid, psync_syncid_t syncid, const char *localpath, psync_fileorfolderid_t remoteid){
|
|
if (eventthreadrunning){
|
|
char *remotepath;
|
|
if (eventid&PEVENT_TYPE_FOLDER)
|
|
remotepath=psync_get_path_by_folderid(remoteid, NULL);
|
|
else
|
|
remotepath=psync_get_path_by_fileid(remoteid, NULL);
|
|
if (unlikely_log(!remotepath))
|
|
return;
|
|
psync_send_event_by_path(eventid, syncid, localpath, remoteid, remotepath);
|
|
psync_free(remotepath);
|
|
}
|
|
}
|
|
|
|
void psync_send_event_by_path(psync_eventtype_t eventid, psync_syncid_t syncid, const char *localpath, psync_fileorfolderid_t remoteid, const char *remotepath){
|
|
if (eventthreadrunning){
|
|
event_list_t *event;
|
|
size_t llen, rlen, slen;
|
|
char *lcopy, *rcopy, *strct, *name;
|
|
llen=strlen(localpath)+1;
|
|
rlen=strlen(remotepath)+1;
|
|
if (eventid&PEVENT_TYPE_FOLDER)
|
|
slen=sizeof(psync_file_event_t);
|
|
else
|
|
slen=sizeof(psync_folder_event_t);
|
|
event=(event_list_t *)psync_malloc(sizeof(event_list_t)+slen+llen+rlen);
|
|
strct=(char *)(event+1);
|
|
lcopy=strct+slen;
|
|
rcopy=lcopy+llen;
|
|
memcpy(lcopy, localpath, llen);
|
|
memcpy(rcopy, remotepath, rlen);
|
|
name=strrchr(rcopy, '/')+1;
|
|
if (eventid&PEVENT_TYPE_FOLDER){
|
|
psync_folder_event_t *f=(psync_folder_event_t *)strct;
|
|
f->folderid=remoteid;
|
|
f->name=name;
|
|
f->localpath=lcopy;
|
|
f->remotepath=rcopy;
|
|
f->syncid=syncid;
|
|
event->data.folder=f;
|
|
}
|
|
else{
|
|
psync_file_event_t *f=(psync_file_event_t *)strct;
|
|
f->fileid=remoteid;
|
|
f->name=name;
|
|
f->localpath=lcopy;
|
|
f->remotepath=rcopy;
|
|
f->syncid=syncid;
|
|
event->data.file=f;
|
|
}
|
|
event->event=eventid;
|
|
event->freedata=0;
|
|
pthread_mutex_lock(&eventmutex);
|
|
psync_list_add_tail(&eventlist, &event->list);
|
|
pthread_cond_signal(&eventcond);
|
|
pthread_mutex_unlock(&eventmutex);
|
|
}
|
|
}
|
|
|
|
void psync_send_eventid(psync_eventtype_t eventid){
|
|
if (eventthreadrunning){
|
|
event_list_t *event;
|
|
|
|
event=psync_new(event_list_t);
|
|
event->data.ptr=NULL;
|
|
event->event=eventid;
|
|
event->freedata=0;
|
|
|
|
pthread_mutex_lock(&eventmutex);
|
|
psync_list_add_tail(&eventlist, &event->list);
|
|
pthread_cond_signal(&eventcond);
|
|
pthread_mutex_unlock(&eventmutex);
|
|
}
|
|
}
|
|
|
|
void psync_send_eventdata(psync_eventtype_t eventid, void *eventdata){
|
|
if (eventthreadrunning){
|
|
event_list_t *event;
|
|
|
|
event=psync_new(event_list_t);
|
|
event->data.ptr=eventdata;
|
|
event->event=eventid;
|
|
event->freedata=1;
|
|
|
|
pthread_mutex_lock(&eventmutex);
|
|
psync_list_add_tail(&eventlist, &event->list);
|
|
pthread_cond_signal(&eventcond);
|
|
pthread_mutex_unlock(&eventmutex);
|
|
}
|
|
else
|
|
psync_free(eventdata);
|
|
}
|
|
|
|
data_event_callback data_event_fptr = NULL;
|
|
|
|
void psync_init_data_event(void *ptr) {
|
|
data_event_fptr = (data_event_callback)ptr;
|
|
debug(D_NOTICE, "Data event handler set.");
|
|
}
|
|
|
|
void data_event_thread(void* ptr) {
|
|
event_data_struct* data = (event_data_struct*)ptr;
|
|
|
|
debug(D_NOTICE, "Sending data event Event id: [%d] Str1: [%s], Str1: [%s], Uint1:[%lu] Uint2:[%lu]", data->eventid, data->str1, data->str2, data->uint1, data->uint2);
|
|
|
|
data_event_fptr(data->eventid, (char *)data->str1, (char *)data->str2, data->uint1, data->uint2);
|
|
|
|
psync_free(ptr);
|
|
}
|
|
|
|
void psync_send_data_event(event_data_struct* data) {
|
|
event_data_struct *event_data;
|
|
|
|
if (data_event_fptr) {
|
|
event_data = psync_new(event_data_struct);
|
|
event_data->eventid = data->eventid;
|
|
event_data->uint1 = data->uint1;
|
|
event_data->uint2 = data->uint2;
|
|
event_data->str1 = strdup(data->str1);
|
|
event_data->str2 = strdup(data->str2);
|
|
|
|
psync_run_thread1("Data Event", data_event_thread, event_data);
|
|
}
|
|
else {
|
|
debug(D_ERROR, "Data event callback function not set.");
|
|
}
|
|
}
|
|
|
|
void psync_data_event_test(int eventid, char* str1, char* str2, uint64_t uint1, uint64_t uint2) {
|
|
debug(D_NOTICE, "Test Data event callback. eventid [%d]. String1: [%s], String2: [%s], uInt1: [%lu] uInt2: [%lu]", eventid, str1, str2, uint1, uint2);
|
|
}
|
|
|