Chat-O-Matic/libs/libdownload/ActionDownload.cpp

291 lines
6.3 KiB
C++

/*
* Copyright 2010, Andrea Anzani. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Andrea Anzani, andrea.anzani@gmail.com
*/
#include <File.h>
#include <Path.h>
#include <Mime.h>
#include <NodeInfo.h>
#include "ActionDownload.h"
#include "DownloadManager.h"
ActionDownload::ActionDownload(BString URL,
entry_ref dest,
bool allowResume,
BLooper* target,
uint32 msg)
:
Action(),
fUrl(URL),
fDest(dest),
fTarget(target),
fMsg(msg),
fAllowResume(allowResume)
{
file = NULL;
curl = NULL;
resuming = 0.0;
SetShouldStop(false);
}
ActionDownload::~ActionDownload()
{
}
void
ActionDownload::SetDownloadManager(DownloadManager* downloadManager)
{
fDownloadManager = downloadManager;
}
status_t
ActionDownload::openFile(BMessage* errors)
{
uint32 openMode = 0;
if (fAllowResume)
openMode = B_WRITE_ONLY | B_CREATE_FILE | B_OPEN_AT_END;
else
openMode = B_WRITE_ONLY | B_CREATE_FILE | B_ERASE_FILE;
if (file) return B_ERROR;
file = new BFile(&fDest, openMode);
if (!file || file->InitCheck() != B_OK) {
BString err("Can't open file!");
errors->AddInt32("status", ERROR_OPENFILE);
errors->AddString("error", err);
return B_ERROR;
}
return B_OK;
}
status_t
ActionDownload::Perform(BMessage* errors)
{
//is it these the right place?
fillMessage(this, errors);
//
CURLcode res;
status_t status = B_OK;
curl = curl_easy_init();
if (!curl) {
BString err("Internal error (CURL can't' init)!");
errors->AddInt32("status", ERROR_CURL_INIT);
errors->AddString("error", err);
return B_ERROR;
}
curl_easy_setopt(curl, CURLOPT_URL, fUrl.String());
curl_easy_setopt(curl, CURLOPT_PROGRESSFUNCTION, callback);
curl_easy_setopt(curl, CURLOPT_PROGRESSDATA, this );
curl_easy_setopt(curl, CURLOPT_NOPROGRESS, FALSE);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, save_file);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, this);
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION , 1); //follow the white link!
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER , FALSE); //https
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST , FALSE); //https
curl_easy_setopt(curl, CURLOPT_FAILONERROR , 1);
if (fDownloadManager)
fDownloadManager->FinishCurl(curl);
//RESUME
if (fAllowResume) {
BNode stat(&fDest);
// file->Seek(SEEK_END,0);
// size_t resume = (size_t)file->Position();
off_t size = 0;
stat.GetSize(&size);
off_t resume = size;
if ( resume > 0 ) {
curl_easy_setopt(curl, CURLOPT_RESUME_FROM, resume);
resuming = (double)resume;
}
}
BMessage connect(*errors);
connect.what = fMsg;
connect.AddInt32("status", OK_CONNECTING);
fTarget->PostMessage(&connect);
res = curl_easy_perform(curl);
if (res != 0) {
BString err("Can't connect!");
errors->AddInt32("status", ERROR_PERFORMING);
errors->AddString("error", err);
errors->AddInt32("curl_error", res);
errors->AddBool("should_stop", ShouldStop());
status = B_ERROR;
} else {
errors->AddInt32("status", OK_DOWNLOADED);
}
curl_easy_cleanup(curl);
errors->AddString("path", GetLocalPath());
errors->AddRef("entry", &fDest);
BMimeType mime;
if (fFileType != "")
BNodeInfo(file).SetType(fFileType.String());
else {
status_t guess = BMimeType::GuessMimeType(&fDest, &mime); // :( return B_ERROR.. why????????????
if (guess == B_OK)
BNodeInfo(file).SetType(mime.Type());
}
delete file;
file = NULL;
if (res == CURLE_BAD_DOWNLOAD_RESUME || res == CURLE_HTTP_RANGE_ERROR) {
// sarebbe bello chiedere.. va be per ora ci riprova e basta!
fAllowResume = false;
errors->MakeEmpty();
Perform(errors);
}
return status;
}
BString
ActionDownload::GetDescription()
{
return "HTTP File download";
}
BString
ActionDownload::GetLocalPath()
{
return BPath(&fDest).Path();
}
void
ActionDownload::fillMessage(ActionDownload* ad, BMessage* msg)
{
//fill with common fileds
int32 what = msg->what; //preserve original what.
*msg = ad->extraInfo;
msg->AddInt64("sid", ad->GetActionID());
msg->AddString("url", ad->fUrl);
msg->what = what;
}
void
ActionDownload::sendProgressX(ActionDownload* ad, double max, double current)
{
BMessage msg(ad->fMsg);
fillMessage(ad, &msg);
msg.AddInt32("status", OK_PROGRESS);
msg.AddFloat("max_progress", (float)max);
msg.AddFloat("current_progress", (float)current);
float div = 0.0;
if (max > 0.0)
div = ((float)current / (float)max ) * 100;
msg.AddInt32("percentage_progress", (int32)floor(div));
double speed = 0;
if (curl_easy_getinfo(ad->curl, CURLINFO_SPEED_DOWNLOAD, &speed) == CURLE_OK)
msg.AddFloat("download_speed", (float)speed);
//TOTALE:
if (curl_easy_getinfo(ad->curl, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &speed) == CURLE_OK)
msg.AddFloat("total_size", (float)speed);
// ATTUALE: CURLINFO_SIZE_DOWNLOAD
if (curl_easy_getinfo(ad->curl, CURLINFO_SIZE_DOWNLOAD, &speed) == CURLE_OK)
msg.AddFloat("actual_size", (float)speed);
char* array = NULL;
if (curl_easy_getinfo(ad->curl, CURLINFO_EFFECTIVE_URL, &array) == CURLE_OK)
msg.AddString("effective_url", BString(array));
if (ad->fFileType == "" && curl_easy_getinfo(ad->curl, CURLINFO_CONTENT_TYPE, &array) == CURLE_OK) {
ad->fFileType.SetTo(array);
BNodeInfo(ad->file).SetType(ad->fFileType.String());
msg.AddString("filetype", BString(array));
}
ad->fTarget->PostMessage(&msg);
}
/*
void
ActionDownload::sendError(BLooper* looper,uint32 amsg,Status st,BString descr){
BMessage msg(amsg);
msg.AddInt32("status",st);
msg.AddString("error",descr);
looper->PostMessage(&msg);
}
*/
//
int
ActionDownload::callback(void* data,
double dltotal,
double dlnow,
double ,
double )
{
ActionDownload* ad = ((ActionDownload*)data);
if (ad->fTarget)
sendProgressX(ad, ad->resuming + dltotal, ad->resuming + dlnow);
return 0;
}
size_t
ActionDownload::save_file( void* ptr, size_t size, size_t nmemb, void* data)
{
ActionDownload* ad = ((ActionDownload*)data);
if (!ad->file) {
BMessage errors; //FIIX: how to report these errors??
if (ad->openFile(&errors) != B_OK) {
printf("ERROR: can't create destination file!\n");
return 0;
}
}
BFile* file = ad->file;
if (ad->ShouldStop())
return 0;
return file->Write(ptr, size * nmemb);
}