/*
* SyncManager.m
* RhoSyncClient
*
* Copyright (C) 2008 Rhomobile, Inc. All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#import
#import
#import
#import
#import
#import
#include "common/RhoPort.h"
#import "logging/RhoLog.h"
#undef DEFAULT_LOGCATEGORY
#define DEFAULT_LOGCATEGORY "Net"
//extern int logged_in();
extern void rho_net_impl_deleteAllCookies();
extern NSString *get_session();
//extern int has_network_impl();
extern int isNetworkAvailableFlags(SCNetworkReachabilityFlags *outFlags);
typedef void (*FSAVECONNDATA)(void* pThis, void* pData);
/*
An enumeration that defines the return values of the network state
of the device.
*/
typedef enum {
NotReachable = 0,
ReachableViaCarrierDataNetwork,
ReachableViaWiFiNetwork
} NetworkStatus;
/*
* Pulls the latest object_values list
* for a given source and populates a list
* of sync objects in memory and the database.
*/
/*
char *fetch_remote_data(char *url_string) {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSString *session;
session = get_session(url_string);
if (session || strstr(url_string, "clientcreate")) {
printf("Fetching data from %s\n", url_string);
char *ret_data;
[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
NSURL *url = [NSURL URLWithString:[[[NSString alloc] initWithUTF8String:url_string] autorelease]];
NSMutableURLRequest *request = [[[NSMutableURLRequest alloc] init] autorelease];
NSError *error = nil;
NSHTTPURLResponse* response;
[request setURL:url];
[request setHTTPMethod:@"GET"];
if (session) {
[request setValue:session forHTTPHeaderField:@"Cookie"];
}
NSString *logData;
NSData *data = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
NSInteger code = [response statusCode];
NSInteger errorCode = [error code];
code = [response statusCode];
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
if (code != 200) {
NSLog(@"An error occured connecting to the sync source: %i returned", code);
if (errorCode == NSURLErrorUserCancelledAuthentication ||
errorCode == NSURLErrorUserAuthenticationRequired) {
logout();
}
[pool drain];
[pool release];
return NULL;
} else {
logData = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
}
ret_data = str_assign((char *)[logData UTF8String]);
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
[pool drain];
[pool release];
return ret_data;
} else {
[pool drain];
[pool release];
return NULL;
}
}*/
/*
* Login with given login/password
* If return success if cookie is stored into shared
* cookie storage NSHTTPCookieStorage
*/
/*
int login(const char *login, const char *password) {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
pSource *source_list;
NSString *session;
int retval = 0, i, source_length, cookie_size = 0;
source_list = malloc(MAX_SOURCES*sizeof(pSource));
source_length = get_sources_from_database(source_list, get_database(), MAX_SOURCES);
for(i = 0; i < source_length; i++) {
char login_string[4096] = "";
sprintf(login_string,
"%s/client_login",
source_list[i]->_source_url);
session = get_session(source_list[i]->_source_url);
if (!session) {
[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
NSMutableURLRequest *request = [[[NSMutableURLRequest alloc] init] autorelease];
NSError *error = [[[NSError alloc] init] autorelease];
NSHTTPURLResponse *response = [[[NSHTTPURLResponse alloc] init] autorelease];
NSString *linkString = [[NSString alloc] initWithUTF8String:login_string];
NSMutableString *postBody = [[NSMutableString alloc] initWithString:@"login="];
[postBody appendString:[[NSString alloc] initWithUTF8String:login]];
[postBody appendString:@"&password="];
[postBody appendString:[[NSString alloc] initWithUTF8String:password]];
[postBody appendString:@"&remember_me=1"];
[request setURL:[NSURL URLWithString:linkString]];
[request setHTTPMethod:@"POST"];
[request setHTTPBody:[postBody dataUsingEncoding:NSUTF8StringEncoding]];
NSURLConnection *conn=[[NSURLConnection alloc] initWithRequest:request delegate:nil];
if (conn) {
[NSURLConnection sendSynchronousRequest: request returningResponse:&response error: &error];
NSInteger errorCode = [error code];
NSInteger code = [response statusCode];
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
if (code != 200) {
NSLog(@"An error occured connecting to the sync source: %i returned", code);
if (errorCode == NSURLErrorUserCancelledAuthentication ||
errorCode == NSURLErrorUserAuthenticationRequired ||
errorCode == NSURLErrorBadServerResponse) {
logout();
}
} else {
NSHTTPCookieStorage *store = [NSHTTPCookieStorage sharedHTTPCookieStorage];
NSArray *cookies = [NSHTTPCookie cookiesWithResponseHeaderFields:[(NSHTTPURLResponse*)response allHeaderFields]
forURL:[NSURL URLWithString:linkString]];
NSEnumerator *c = [cookies objectEnumerator];
id cookie;
while (cookie = [c nextObject]) {
NSLog(@"Storing Cookie: Name: %@, Value: %@",
[cookie name],
[cookie value]);
[store setCookie:cookie];
cookie_size++;
}
}
}
} else {
printf("Found existing session for url...\n");
}
}
if (cookie_size == 0) {
retval = 0;
} else {
retval = 1;
}
[pool drain];
[pool release];
return retval;
}*/
//static NSURLConnection* g_curConn = NULL;
char* rho_net_impl_requestCookies(const char* szMethod, const char* szUrl, const char* szBody, int* pnRespCode, FSAVECONNDATA fSave, void* pThis )
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSString *session;
int cookie_size = 0;
if ( pnRespCode )
*pnRespCode = -1;
char* respData = NULL;
size_t data_size = szBody != NULL ? strlen(szBody) : 0;
session = get_session(szUrl);
if (!session)
{
RAWLOG_INFO1("Request cookies by Url: %s", szUrl);
[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
NSMutableURLRequest *request = [[[NSMutableURLRequest alloc] init] autorelease];
NSError *error = nil;
NSHTTPURLResponse *response;
NSString *linkString = [[NSString alloc] initWithUTF8String:szUrl];
NSMutableData *postBody = [NSMutableData dataWithBytes:szBody length:data_size];
[request setURL:[NSURL URLWithString:linkString]];
[request setHTTPMethod:[[NSString alloc] initWithUTF8String:szMethod]];
[request setValue:@"application/x-www-form-urlencoded" forHTTPHeaderField:@"Content-Type"];
[request setHTTPBody:postBody];
[request setTimeoutInterval:180];
NSURLConnection *conn=[[NSURLConnection alloc] initWithRequest:request delegate:nil];
if (conn)
{
(*fSave)(pThis,conn);
NSData *returnData = [NSURLConnection sendSynchronousRequest: request returningResponse:&response error: &error];
NSInteger errorCode = [error code];
NSInteger code = [response statusCode];
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
(*fSave)(pThis,NULL);
if ( pnRespCode )
*pnRespCode = code;
NSString* strData = [[NSString alloc] initWithData:returnData encoding:NSUTF8StringEncoding];
if ( code !=500 && code !=422)
respData = str_assign( (char *)[strData UTF8String] );
if (code != 200)
{
RAWLOG_ERROR4("Request cookies failed. HTTP Code: %d returned. HTTP Response: %s. NSError: %d. NSErrorInfo : %s",
code, [strData UTF8String], errorCode, [[error localizedDescription] UTF8String]);
if (errorCode == NSURLErrorUserCancelledAuthentication ||
errorCode == NSURLErrorUserAuthenticationRequired ||
errorCode == NSURLErrorBadServerResponse )
{
if ( pnRespCode )
*pnRespCode = 401;
rho_net_impl_deleteAllCookies();
}
} else
{
NSHTTPCookieStorage *store = [NSHTTPCookieStorage sharedHTTPCookieStorage];
NSArray *cookies = [NSHTTPCookie cookiesWithResponseHeaderFields:[(NSHTTPURLResponse*)response allHeaderFields]
forURL:[NSURL URLWithString:linkString]];
NSEnumerator *c = [cookies objectEnumerator];
id cookie;
while (cookie = [c nextObject])
{
RAWLOG_INFO2("Storing Cookie: Name: %s, Value: %s", [[cookie name] UTF8String], [[cookie value] UTF8String]);
[store setCookie:cookie];
cookie_size++;
}
}
}
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
} else {
RAWLOG_INFO("Found existing session for url...");
if ( pnRespCode )
*pnRespCode = 200;
}
[pool drain];
[pool release];
return respData;
}
char* rho_net_impl_request(const char* szMethod, const char* szUrl, const char* szBody, int* pnRespCode, FSAVECONNDATA fSave, void* pThis )
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
if ( pnRespCode )
*pnRespCode = -1;
char* respData = NULL;
size_t data_size = szBody != NULL ? strlen(szBody) : 0;
NSString *session = get_session(szUrl);
NSString *linkString = [[NSString alloc] initWithUTF8String:szUrl];
if (session || [linkString hasPrefix:@"http://localhost"] || [linkString hasPrefix:@"http://127.0.0.0"])
{
RAWLOG_INFO1("Request Url: %s", szUrl);
[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
NSMutableURLRequest *request = [[[NSMutableURLRequest alloc] init] autorelease];
NSError *error = nil;
NSHTTPURLResponse *response;
NSMutableData *postBody = [NSMutableData dataWithBytes:szBody length:data_size];
[request setURL:[NSURL URLWithString:linkString]];
[request setHTTPMethod:[[NSString alloc] initWithUTF8String:szMethod]];
[request setValue:@"application/x-www-form-urlencoded" forHTTPHeaderField:@"Content-Type"];
[request setValue:session forHTTPHeaderField:@"Cookie"];
[request setHTTPBody:postBody];
[request setTimeoutInterval:180];
NSURLConnection *conn=[[NSURLConnection alloc] initWithRequest:request delegate:nil];
if (conn)
{
(*fSave)(pThis,conn);
NSData *returnData = [ NSURLConnection sendSynchronousRequest: request returningResponse:&response error: &error ];
NSInteger errorCode = [error code];
NSInteger code = [response statusCode];
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
(*fSave)(pThis,NULL);
if ( pnRespCode )
*pnRespCode = code;
NSString* strData = [[NSString alloc] initWithData:returnData encoding:NSUTF8StringEncoding];
if (code!=500 && code !=422)
respData = str_assign( (char *)[strData UTF8String] );
if (code != 200)
{
RAWLOG_ERROR4("Request failed. HTTP Code: %d returned. HTTP Response: %s. NSError: %d. NSErrorInfo : %s",
code, [strData UTF8String], errorCode, [[error localizedDescription] UTF8String]);
if (errorCode == NSURLErrorUserCancelledAuthentication ||
errorCode == NSURLErrorUserAuthenticationRequired)
{
if ( pnRespCode )
*pnRespCode = 401;
rho_net_impl_deleteAllCookies();
}
} else
{
RAWTRACE("RESPONSE-----");
RAWTRACE(respData);
RAWTRACE("END RESPONSE-----");
}
}
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
}
[pool drain];
[pool release];
return respData;
}
char* rho_net_impl_pullFile(const char* szUrl, int* pnRespCode, int (*writeFunc)(void* pThis, void* pData, int nSize), void* pThisFile, FSAVECONNDATA fSave, void* pThis)
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
if ( pnRespCode )
*pnRespCode = -1;
char* respData = NULL;
NSString *linkString = [[NSString alloc] initWithUTF8String:szUrl];
{
RAWLOG_INFO1("Request Url: %s", szUrl);
[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
NSMutableURLRequest *request = [[[NSMutableURLRequest alloc] init] autorelease];
NSError *error = nil;
NSHTTPURLResponse *response;
[request setURL:[NSURL URLWithString:linkString]];
[request setTimeoutInterval:180];
NSURLConnection *conn=[[NSURLConnection alloc] initWithRequest:request delegate:nil];
if (conn)
{
(*fSave)(pThis,conn);
NSData *returnData = [ NSURLConnection sendSynchronousRequest: request returningResponse:&response error: &error ];
NSInteger errorCode = [error code];
NSInteger code = [response statusCode];
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
(*fSave)(pThis,NULL);
if ( pnRespCode )
*pnRespCode = code;
if (code != 200)
{
NSString* strData = [[NSString alloc] initWithData:returnData encoding:NSUTF8StringEncoding];
if (code!=500 && code !=422)
respData = str_assign( (char *)[strData UTF8String] );
RAWLOG_ERROR4("Request failed. HTTP Code: %d returned. HTTP Response: %s. NSError: %d. NSErrorInfo : %s",
code, [strData UTF8String], errorCode, [[error localizedDescription] UTF8String]);
if (errorCode == NSURLErrorUserCancelledAuthentication ||
errorCode == NSURLErrorUserAuthenticationRequired)
{
if ( pnRespCode )
*pnRespCode = 401;
rho_net_impl_deleteAllCookies();
}
} else
{
(*writeFunc)(pThisFile, [returnData bytes], [returnData length]);
}
}
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
}
[pool drain];
[pool release];
return respData;
}
int rho_net_impl_pushFile(const char* szUrl, const char* szFilePath, int* pbRespRecieved, FSAVECONNDATA fSave, void* pThis)
{
//TODO: test rho_net_impl_pushFile
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
if ( pbRespRecieved )
*pbRespRecieved = 0;
int nRet = 0;
NSString *session = get_session(szUrl);
if (session)
{
RAWLOG_INFO2("Push file. Url: %s; File path: %s", szUrl, szFilePath);
[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
NSMutableURLRequest *request = [[[NSMutableURLRequest alloc] init] autorelease];
NSError *error = nil;
NSHTTPURLResponse *response;
NSString *linkString = [[NSString alloc] initWithUTF8String:szUrl];
[request setURL:[NSURL URLWithString:linkString]];
[request setValue:session forHTTPHeaderField:@"Cookie"];
[request setHTTPBodyStream:[NSInputStream inputStreamWithFileAtPath:[[NSString alloc] initWithUTF8String:szFilePath]]];
[request setTimeoutInterval:180];
NSURLConnection *conn=[[NSURLConnection alloc] initWithRequest:request delegate:nil];
if (conn)
{
(*fSave)(pThis,conn);
NSData *returnData = [ NSURLConnection sendSynchronousRequest: request returningResponse:&response error: &error ];
NSInteger errorCode = [error code];
NSInteger code = [response statusCode];
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
(*fSave)(pThis,NULL);
if ( pbRespRecieved )
*pbRespRecieved = code > 0;
NSString* strData = [[NSString alloc] initWithData:returnData encoding:NSUTF8StringEncoding];
if (code != 200)
{
RAWLOG_ERROR4("Push file failed. HTTP Code: %d returned. HTTP Response: %s. NSError: %d. NSErrorInfo : %s",
code, [strData UTF8String], errorCode, [[error localizedDescription] UTF8String]);
if (errorCode == NSURLErrorUserCancelledAuthentication ||
errorCode == NSURLErrorUserAuthenticationRequired)
{
rho_net_impl_deleteAllCookies();
}
} else
nRet = 1;
}
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
}
[pool drain];
[pool release];
return nRet;
}
void rho_net_impl_cancel(void* pConnData)
{
NSURLConnection* pConn = (NSURLConnection*)pConnData;
//if ( pConn != NULL )
// [pConn cancel];
}
/*
* Pushes changes from list to rhosync server
*/
char* rho_net_impl_pushMultipartData(const char* url, const char* data, size_t data_size, int* pnRespCode, FSAVECONNDATA fSave, void* pThis)
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
char* respData = NULL;
if ( pnRespCode )
*pnRespCode = -1;
NSString *session = get_session(url);
if (session) {
RAWLOG_INFO2("Push data. Url: %s; Size: %d", url, data_size);
[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
NSMutableURLRequest *request = [[[NSMutableURLRequest alloc] init] autorelease];
NSError *error = nil;
NSHTTPURLResponse *response;
NSString *linkString = [[NSString alloc] initWithUTF8String:url];
NSMutableData *postBody = [NSMutableData dataWithBytes:data length:data_size];
[request setURL:[NSURL URLWithString:linkString]];
[request setHTTPMethod:@"POST"];
[request setValue:session forHTTPHeaderField:@"Cookie"];
[request setHTTPBody:postBody];
[request setTimeoutInterval:180];
//if (contentType){
// NSString *temp = [[NSString alloc] initWithUTF8String:contentType];
//[request setValue:temp forHTTPHeaderField:@"Content-Type"];
[request setValue:@"multipart/form-data; boundary=----------A6174410D6AD474183FDE48F5662FCC5" forHTTPHeaderField:@"Content-Type"];
//}
NSURLConnection *conn=[[NSURLConnection alloc] initWithRequest:request delegate:nil];
if (conn) {
(*fSave)(pThis,conn);
NSData *returnData = [ NSURLConnection sendSynchronousRequest: request returningResponse:&response error: &error ];
NSInteger errorCode = [error code];
NSInteger code = [response statusCode];
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
(*fSave)(pThis,NULL);
if ( pnRespCode )
*pnRespCode = code;
NSString* strData = [[NSString alloc] initWithData:returnData encoding:NSUTF8StringEncoding];
if(code!=500 && code !=422)
respData = str_assign( (char *)[strData UTF8String] );
if (code != 200)
{
RAWLOG_ERROR4("Push file failed. HTTP Code: %d returned. HTTP Response: %s. NSError: %d. NSErrorInfo : %s",
code, [strData UTF8String], errorCode, [[error localizedDescription] UTF8String]);
if (errorCode == NSURLErrorUserCancelledAuthentication ||
errorCode == NSURLErrorUserAuthenticationRequired)
{
if ( pnRespCode )
*pnRespCode = 401;
rho_net_impl_deleteAllCookies();
}
} else
{
RAWTRACE("RESPONSE-----");
RAWTRACE(respData);
RAWTRACE("END RESPONSE-----");
}
}
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
}
[pool drain];
[pool release];
return respData;
}
NSArray *get_all_cookies()
{
NSHTTPCookieStorage *cookieStore = [NSHTTPCookieStorage sharedHTTPCookieStorage];
NSArray *cookies = [cookieStore cookies];
return cookies;
}
int rho_sync_logged_in_cookies()
{
int i,retval = 0;
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSArray *cookies = get_all_cookies();
int count = [cookies count];
// Iterate over all cookies and see if we have a rhosync_session
for (i = 0; i < count; i++) {
if ([[[cookies objectAtIndex:i] name] isEqualToString:@"rhosync_session"]) retval = 1;
}
[pool drain];
[pool release];
return retval;
}
void rho_net_impl_deleteAllCookies()
{
int i;
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSArray *cookies = get_all_cookies();
int count = [cookies count];
for (i = 0; i < count; i++) {
NSHTTPCookie *cookie = [cookies objectAtIndex:i];
NSString *name = [cookie name];
if ([name isEqualToString:@"rhosync_session"] || [name isEqualToString:@"auth_token"]) {
[[NSHTTPCookieStorage sharedHTTPCookieStorage] deleteCookie:cookie];
}
}
[pool drain];
[pool release];
}
/*
* Retrieve cookie from shared cookie storage
*/
NSString *get_session(char *url_string)
{
NSString *session = NULL;
NSString *ns_url_string = [[[NSString alloc] initWithUTF8String:url_string] autorelease];
NSHTTPCookieStorage *cookieStore = [NSHTTPCookieStorage sharedHTTPCookieStorage];
if (cookieStore && ns_url_string) {
NSURL *url = [NSURL URLWithString:[ns_url_string stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
// In the case where url_string is not properly encoded, url will be nil
if(url) {
NSArray *cookies = [cookieStore cookiesForURL:url];
NSString *cookie = [[NSHTTPCookie requestHeaderFieldsWithCookies:cookies] objectForKey:@"Cookie"];
if (cookie) {
session = [[[NSString alloc] initWithString:cookie] autorelease];
}
}
}
return session;
}
// Determines network connectivity
int rho_net_has_network() {
SCNetworkReachabilityFlags defaultRouteFlags;
int defaultRouteIsAvailable = isNetworkAvailableFlags(&defaultRouteFlags);
if (defaultRouteIsAvailable == 1) {
if (defaultRouteFlags & kSCNetworkReachabilityFlagsIsDirect) {
// Ad-Hoc network, not available
return 0;
}
else if (defaultRouteFlags & ReachableViaCarrierDataNetwork) {
// Cell network available
return 1;
}
// WIFI available
return 1;
}
return 0;
}
int isNetworkAvailableFlags(SCNetworkReachabilityFlags *outFlags)
{
struct sockaddr_in zeroAddress;
BOOL isReachable = FALSE;
int reachable = 0;
SCNetworkReachabilityRef defaultRouteReachability;
NSString *hostNameOrAddress;
bzero(&zeroAddress, sizeof(zeroAddress));
zeroAddress.sin_len = sizeof(zeroAddress);
zeroAddress.sin_family = AF_INET;
defaultRouteReachability = SCNetworkReachabilityCreateWithAddress(NULL, (struct sockaddr *)&zeroAddress);
hostNameOrAddress = @"0.0.0.0";
SCNetworkReachabilityFlags flags;
BOOL gotFlags = SCNetworkReachabilityGetFlags(defaultRouteReachability, &flags);
if (!gotFlags) {
CFRelease(defaultRouteReachability);
return reachable;
}
isReachable = flags & kSCNetworkReachabilityFlagsReachable;
BOOL noConnectionRequired = !(flags & kSCNetworkReachabilityFlagsConnectionRequired);
if ((flags & kSCNetworkReachabilityFlagsIsWWAN)) {
noConnectionRequired = YES;
}
reachable = (isReachable && noConnectionRequired) ? 1 : 0;
// Callers of this method might want to use the reachability flags, so if an 'out' parameter
// was passed in, assign the reachability flags to it.
if (outFlags) {
*outFlags = flags;
}
CFRelease(defaultRouteReachability);
return reachable;
}