pcloudcc-lneely/lib/pclsync/pcallbacks.c

476 lines
15 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);
}
/**********************************************************************************************/