using System; using System.Text; using System.Collections.Generic; using System.Security.Cryptography; using com.fpnn.proto; using com.fpnn.common; namespace com.fpnn.rtm { public partial class RTMClient { private enum FileTokenType { P2P, Group, Room, Upload, } private class SendFileInfo { public FileTokenType actionType; public long xid; public byte mtype; public byte[] fileContent; public string filename; public string fileExtension; public string userAttrs; public long messageId; public string token; public string endpoint; public int remainTimeout; public long lastActionTimestamp; public MessageIdDelegate callback; public SendMessageDelegate callbackMtime; public Action uploadCallback; public Dictionary rtmAttrs; } //===========================[ File Token ]=========================// //-- Action private bool FileToken(Action callback, FileTokenType tokenType, long xid, int timeout = 0) { TCPClient client = GetCoreClient(); if (client == null) return false; Quest quest = new Quest("filetoken"); switch (tokenType) { case FileTokenType.P2P: quest.Param("cmd", "sendfile"); quest.Param("to", xid); break; case FileTokenType.Group: quest.Param("cmd", "sendgroupfile"); quest.Param("gid", xid); break; case FileTokenType.Room: quest.Param("cmd", "sendroomfile"); quest.Param("rid", xid); break; case FileTokenType.Upload: quest.Param("cmd", "uploadfile"); break; } return client.SendQuest(quest, (Answer answer, int errorCode) => { string token = ""; string endpoint = ""; if (errorCode == fpnn.ErrorCode.FPNN_EC_OK) { try { token = answer.Want("token"); endpoint = answer.Want("endpoint"); } catch (Exception) { errorCode = fpnn.ErrorCode.FPNN_EC_CORE_INVALID_PACKAGE; } } callback(token, endpoint, errorCode); }, timeout); } private int FileToken(out string token, out string endpoint, FileTokenType tokenType, long xid, int timeout = 0) { token = ""; endpoint = ""; TCPClient client = GetCoreClient(); if (client == null) return fpnn.ErrorCode.FPNN_EC_CORE_INVALID_CONNECTION; Quest quest = new Quest("filetoken"); switch (tokenType) { case FileTokenType.P2P: quest.Param("cmd", "sendfile"); quest.Param("to", xid); break; case FileTokenType.Group: quest.Param("cmd", "sendgroupfile"); quest.Param("gid", xid); break; case FileTokenType.Room: quest.Param("cmd", "sendroomfile"); quest.Param("rid", xid); break; case FileTokenType.Upload: quest.Param("cmd", "uploadfile"); break; } Answer answer = client.SendQuest(quest, timeout); if (answer.IsException()) return answer.ErrorCode(); try { token = answer.Want("token"); endpoint = answer.Want("endpoint"); return fpnn.ErrorCode.FPNN_EC_OK; } catch (Exception) { return fpnn.ErrorCode.FPNN_EC_CORE_INVALID_PACKAGE; } } //===========================[ IPv4 Convert IPv6 Utilies ]=========================// private string ConvertIPv4ToIPv6(string ipv4) { string[] parts = ipv4.Split(new Char[] { '.' }); if (parts.Length != 4) return string.Empty; foreach (string part in parts) { int partInt = Int32.Parse(part); if (partInt > 255 || partInt < 0) return string.Empty; } string part7 = Convert.ToString(Int32.Parse(parts[0]) * 256 + Int32.Parse(parts[1]), 16); string part8 = Convert.ToString(Int32.Parse(parts[2]) * 256 + Int32.Parse(parts[3]), 16); return "64:ff9b::" + part7 + ":" + part8; } private bool ConvertIPv4EndpointToIPv6IPPort(string ipv4endpoint, out string ipv6, out int port) { int idx = ipv4endpoint.LastIndexOf(':'); if (idx == -1) { ipv6 = string.Empty; port = 0; return false; } string ipv4 = ipv4endpoint.Substring(0, idx); string portString = ipv4endpoint.Substring(idx + 1); port = Convert.ToInt32(portString, 10); ipv6 = ConvertIPv4ToIPv6(ipv4); if (ipv6.Length == 0) return false; return true; } //===========================[ File Utilies ]=========================// private void UpdateTimeout(ref int timeout, ref long lastActionTimestamp) { long currMsec = ClientEngine.GetCurrentMilliseconds(); timeout -= (int)((currMsec - lastActionTimestamp) / 1000); lastActionTimestamp = currMsec; } private string ExtraFileExtension(string filename) { int idx = filename.LastIndexOf('.'); if (idx == -1) return null; return filename.Substring(idx + 1); } private string GetMD5(string str, bool upper) { byte[] inputBytes = Encoding.ASCII.GetBytes(str); return GetMD5(inputBytes, upper); } private string GetMD5(byte[] bytes, bool upper) { MD5 md5 = MD5.Create(); byte[] hash = md5.ComputeHash(bytes); string f = "x2"; if (upper) { f = "X2"; } StringBuilder sb = new StringBuilder(); for (int i = 0; i < hash.Length; i++) { sb.Append(hash[i].ToString(f)); } return sb.ToString(); } private string BuildFileAttrs(SendFileInfo info) { string fileMD5 = GetMD5(info.fileContent, false); string sign = GetMD5(fileMD5 + ":" + info.token, false); if (info.rtmAttrs == null) info.rtmAttrs = new Dictionary(); Dictionary rtmAttrs = info.rtmAttrs; rtmAttrs.Add("sign", sign); if (info.filename != null && info.filename.Length > 0) { rtmAttrs.Add("filename", info.filename); if (info.fileExtension == null || info.fileExtension.Length == 0) info.fileExtension = ExtraFileExtension(info.filename); } if (info.fileExtension != null && info.fileExtension.Length > 0) rtmAttrs.Add("ext", info.fileExtension); Dictionary fileAttrs = new Dictionary(); fileAttrs.Add("rtm", rtmAttrs); if (info.userAttrs == null || info.userAttrs.Length == 0) fileAttrs.Add("custom", ""); else { try { Dictionary userDict = Json.ParseObject(info.userAttrs); if (userDict != null) fileAttrs.Add("custom", userDict); else fileAttrs.Add("custom", info.userAttrs); } catch (JsonException) { fileAttrs.Add("custom", info.userAttrs); } } return Json.ToString(fileAttrs); } private Quest BuildSendFileQuest(out long messageId, SendFileInfo info) { Quest quest = null; switch (info.actionType) { case FileTokenType.P2P: quest = new Quest("sendfile"); quest.Param("to", info.xid); break; case FileTokenType.Group: quest = new Quest("sendgroupfile"); quest.Param("gid", info.xid); break; case FileTokenType.Room: quest = new Quest("sendroomfile"); quest.Param("rid", info.xid); break; case FileTokenType.Upload: quest = new Quest("uploadfile"); quest.Param("uid", uid); break; } quest.Param("pid", projectId); quest.Param("from", uid); quest.Param("token", info.token); quest.Param("mtype", info.mtype); messageId = info.messageId; if (info.messageId == 0) messageId = MidGenerator.Gen(); quest.Param("mid", messageId); quest.Param("file", info.fileContent); quest.Param("attrs", BuildFileAttrs(info)); return quest; } private int SendFileWithClient(SendFileInfo info, TCPClient client) { UpdateTimeout(ref info.remainTimeout, ref info.lastActionTimestamp); if (info.remainTimeout <= 0) return fpnn.ErrorCode.FPNN_EC_CORE_TIMEOUT; long messageId = 0; Quest quest = BuildSendFileQuest(out messageId, info); bool success = client.SendQuest(quest, (Answer answer, int errorCode) => { if (errorCode == fpnn.ErrorCode.FPNN_EC_OK) { try { if (info.callbackMtime == null) info.callback(messageId, fpnn.ErrorCode.FPNN_EC_OK); else { long mtime = answer.Want("mtime"); info.callbackMtime(messageId, mtime, fpnn.ErrorCode.FPNN_EC_OK); } RTMControlCenter.ActiveFileGateClient(info.endpoint, client); return; } catch (Exception) { errorCode = fpnn.ErrorCode.FPNN_EC_CORE_INVALID_PACKAGE; } } if (info.callbackMtime == null) info.callback(0, errorCode); else info.callbackMtime(0, 0, errorCode); }, info.remainTimeout); if (success) return fpnn.ErrorCode.FPNN_EC_OK; else return fpnn.ErrorCode.FPNN_EC_CORE_INVALID_CONNECTION; } private int UploadFileWithClient(SendFileInfo info, TCPClient client) { UpdateTimeout(ref info.remainTimeout, ref info.lastActionTimestamp); if (info.remainTimeout <= 0) return fpnn.ErrorCode.FPNN_EC_CORE_TIMEOUT; long messageId = 0; Quest quest = BuildSendFileQuest(out messageId, info); bool success = client.SendQuest(quest, (Answer answer, int errorCode) => { if (errorCode == fpnn.ErrorCode.FPNN_EC_OK) { try { string url = answer.Want("url"); uint size = answer.Want("size"); info.uploadCallback(url, size, fpnn.ErrorCode.FPNN_EC_OK); RTMControlCenter.ActiveFileGateClient(info.endpoint, client); return; } catch (Exception) { errorCode = fpnn.ErrorCode.FPNN_EC_CORE_INVALID_PACKAGE; } } info.uploadCallback(null, 0, errorCode); }, info.remainTimeout); if (success) return fpnn.ErrorCode.FPNN_EC_OK; else return fpnn.ErrorCode.FPNN_EC_CORE_INVALID_CONNECTION; } private int SendFileWithoutClient(SendFileInfo info, bool originalEndpoint) { string fileGateEndpoint; if (originalEndpoint) fileGateEndpoint = info.endpoint; else { if (ConvertIPv4EndpointToIPv6IPPort(info.endpoint, out string ipv6, out int port)) { fileGateEndpoint = ipv6 + ":" + port; } else { return fpnn.ErrorCode.FPNN_EC_CORE_INVALID_CONNECTION; } } TCPClient client = TCPClient.Create(fileGateEndpoint, true); if (errorRecorder != null) client.SetErrorRecorder(errorRecorder); client.SetConnectionConnectedDelegate((Int64 connectionId, string endpoint, bool connected) => { int errorCode = fpnn.ErrorCode.FPNN_EC_OK; if (connected) { RTMControlCenter.ActiveFileGateClient(info.endpoint, client); errorCode = SendFileWithClient(info, client); } else if (originalEndpoint) { errorCode = SendFileWithoutClient(info, false); } else { errorCode = fpnn.ErrorCode.FPNN_EC_CORE_INVALID_CONNECTION; } if (errorCode != fpnn.ErrorCode.FPNN_EC_OK) { if (info.callbackMtime == null) info.callback(0, errorCode); else info.callbackMtime(0, 0, errorCode); } if (connected) client.SetConnectionConnectedDelegate(null); }); client.AsyncConnect(); return fpnn.ErrorCode.FPNN_EC_OK; } private int UploadFileWithoutClient(SendFileInfo info, bool originalEndpoint) { string fileGateEndpoint; if (originalEndpoint) fileGateEndpoint = info.endpoint; else { if (ConvertIPv4EndpointToIPv6IPPort(info.endpoint, out string ipv6, out int port)) { fileGateEndpoint = ipv6 + ":" + port; } else { return fpnn.ErrorCode.FPNN_EC_CORE_INVALID_CONNECTION; } } TCPClient client = TCPClient.Create(fileGateEndpoint, true); if (errorRecorder != null) client.SetErrorRecorder(errorRecorder); client.SetConnectionConnectedDelegate((Int64 connectionId, string endpoint, bool connected) => { int errorCode = fpnn.ErrorCode.FPNN_EC_OK; if (connected) { RTMControlCenter.ActiveFileGateClient(info.endpoint, client); errorCode = UploadFileWithClient(info, client); } else if (originalEndpoint) { errorCode = UploadFileWithoutClient(info, false); } else { errorCode = fpnn.ErrorCode.FPNN_EC_CORE_INVALID_CONNECTION; } if (errorCode != fpnn.ErrorCode.FPNN_EC_OK) info.uploadCallback(null, 0, errorCode); if (connected) client.SetConnectionConnectedDelegate(null); }); client.AsyncConnect(); return fpnn.ErrorCode.FPNN_EC_OK; } private void GetFileTokenCallback(SendFileInfo info, string token, string endpoint, int errorCode) { if (errorCode == fpnn.ErrorCode.FPNN_EC_OK) { info.token = token; info.endpoint = endpoint; TCPClient fileClient = RTMControlCenter.FecthFileGateClient(info.endpoint); if (fileClient != null) errorCode = SendFileWithClient(info, fileClient); else errorCode = SendFileWithoutClient(info, true); if (errorCode == fpnn.ErrorCode.FPNN_EC_OK) return; } else { if (info.callbackMtime == null) info.callback(0, errorCode); else info.callbackMtime(0, 0, errorCode); } } private void GetFileTokenUploadCallback(SendFileInfo info, string token, string endpoint, int errorCode) { if (errorCode == fpnn.ErrorCode.FPNN_EC_OK) { info.token = token; info.endpoint = endpoint; TCPClient fileClient = RTMControlCenter.FecthFileGateClient(info.endpoint); if (fileClient != null) errorCode = UploadFileWithClient(info, fileClient); else errorCode = UploadFileWithoutClient(info, true); if (errorCode == fpnn.ErrorCode.FPNN_EC_OK) return; } else info.uploadCallback(null, 0, errorCode); } //===========================[ Real Send File ]=========================// private bool RealSendFile(MessageIdDelegate callback, FileTokenType tokenType, long targetId, byte mtype, byte[] fileContent, string filename, string fileExtension, string attrs, Dictionary rtmAttrs, long messageId, int timeout) { if (mtype < 40 || mtype > 50) { if (errorRecorder != null) errorRecorder.RecordError("Send file require mtype between [40, 50], current mtype is " + mtype); if (RTMConfig.triggerCallbackIfAsyncMethodReturnFalse) ClientEngine.RunTask(() => { callback(0, ErrorCode.RTM_EC_INVALID_MTYPE); }); return false; } TCPClient client = GetCoreClient(); if (client == null) { if (RTMConfig.triggerCallbackIfAsyncMethodReturnFalse) ClientEngine.RunTask(() => { callback(0, fpnn.ErrorCode.FPNN_EC_CORE_INVALID_CONNECTION); }); return false; } SendFileInfo info = new SendFileInfo { actionType = tokenType, xid = targetId, mtype = mtype, fileContent = fileContent, filename = filename, fileExtension = fileExtension, userAttrs = attrs, messageId = messageId, remainTimeout = timeout, lastActionTimestamp = ClientEngine.GetCurrentMilliseconds(), callback = callback }; info.rtmAttrs = rtmAttrs; bool asyncStarted = FileToken((string token, string endpoint, int errorCode) => { GetFileTokenCallback(info, token, endpoint, errorCode); }, tokenType, info.xid, timeout); if (!asyncStarted && RTMConfig.triggerCallbackIfAsyncMethodReturnFalse) ClientEngine.RunTask(() => { callback(0, fpnn.ErrorCode.FPNN_EC_CORE_INVALID_CONNECTION); }); return asyncStarted; } private bool RealSendFile(SendMessageDelegate callback, FileTokenType tokenType, long targetId, byte mtype, byte[] fileContent, string filename, string fileExtension, string attrs, Dictionary rtmAttrs, long messageId, int timeout) { if (mtype < 40 || mtype > 50) { if (errorRecorder != null) errorRecorder.RecordError("Send file require mtype between [40, 50], current mtype is " + mtype); if (RTMConfig.triggerCallbackIfAsyncMethodReturnFalse) ClientEngine.RunTask(() => { callback(0, 0, ErrorCode.RTM_EC_INVALID_MTYPE); }); return false; } TCPClient client = GetCoreClient(); if (client == null) { if (RTMConfig.triggerCallbackIfAsyncMethodReturnFalse) ClientEngine.RunTask(() => { callback(0, 0, fpnn.ErrorCode.FPNN_EC_CORE_INVALID_CONNECTION); }); return false; } SendFileInfo info = new SendFileInfo { actionType = tokenType, xid = targetId, mtype = mtype, fileContent = fileContent, filename = filename, fileExtension = fileExtension, userAttrs = attrs, messageId = messageId, remainTimeout = timeout, lastActionTimestamp = ClientEngine.GetCurrentMilliseconds(), callbackMtime = callback }; info.rtmAttrs = rtmAttrs; bool asyncStarted = FileToken((string token, string endpoint, int errorCode) => { GetFileTokenCallback(info, token, endpoint, errorCode); }, tokenType, info.xid, timeout); if (!asyncStarted && RTMConfig.triggerCallbackIfAsyncMethodReturnFalse) ClientEngine.RunTask(() => { callback(0, 0, fpnn.ErrorCode.FPNN_EC_CORE_INVALID_CONNECTION); }); return asyncStarted; } private int RealSendFile(out long messageId, out long mtime, FileTokenType tokenType, long targetId, byte mtype, byte[] fileContent, string filename, string fileExtension, string attrs, Dictionary rtmAttrs, long mid, int timeout) { messageId = mid; mtime = 0; //----------[ 1. check mtype ]---------------// if (mtype < 40 || mtype > 50) { if (errorRecorder != null) errorRecorder.RecordError("Send file require mtype between [40, 50], current mtype is " + mtype); return ErrorCode.RTM_EC_INVALID_MTYPE; } //----------[ 2. Get File Token ]---------------// TCPClient client = GetCoreClient(); if (client == null) return fpnn.ErrorCode.FPNN_EC_CORE_INVALID_CONNECTION; long lastActionTimestamp = ClientEngine.GetCurrentMilliseconds(); int errorCode = FileToken(out string token, out string endpoint, tokenType, targetId, timeout); if (errorCode != fpnn.ErrorCode.FPNN_EC_OK) return errorCode; //----------[ 2.1 check timeout ]---------------// UpdateTimeout(ref timeout, ref lastActionTimestamp); if (timeout <= 0) return fpnn.ErrorCode.FPNN_EC_CORE_TIMEOUT; //----------[ 3. fetch file gate client ]---------------// TCPClient fileClient = RTMControlCenter.FecthFileGateClient(endpoint); if (fileClient == null) { fileClient = TCPClient.Create(endpoint, true); if (fileClient.SyncConnect()) { RTMControlCenter.ActiveFileGateClient(endpoint, fileClient); } else { //----------[ 3.1 check timeout ]---------------// UpdateTimeout(ref timeout, ref lastActionTimestamp); if (timeout <= 0) return fpnn.ErrorCode.FPNN_EC_CORE_TIMEOUT; if (ConvertIPv4EndpointToIPv6IPPort(endpoint, out string ipv6, out int port)) { fileClient = TCPClient.Create(ipv6 + ":" + port, true); if (fileClient.SyncConnect()) { RTMControlCenter.ActiveFileGateClient(endpoint, fileClient); } else { return fpnn.ErrorCode.FPNN_EC_CORE_INVALID_CONNECTION; } } else { return fpnn.ErrorCode.FPNN_EC_CORE_INVALID_CONNECTION; } } } //----------[ 3.2 check timeout ]---------------// UpdateTimeout(ref timeout, ref lastActionTimestamp); if (timeout <= 0) return fpnn.ErrorCode.FPNN_EC_CORE_TIMEOUT; //----------[ 4. build quest ]---------------// SendFileInfo info = new SendFileInfo { actionType = tokenType, xid = targetId, mtype = mtype, fileContent = fileContent, filename = filename, fileExtension = fileExtension, userAttrs = attrs, messageId = mid, token = token, }; info.rtmAttrs = rtmAttrs; Quest quest = BuildSendFileQuest(out messageId, info); Answer answer = fileClient.SendQuest(quest, timeout); if (answer.IsException()) return answer.ErrorCode(); RTMControlCenter.ActiveFileGateClient(endpoint, fileClient); mtime = answer.Want("mtime"); return fpnn.ErrorCode.FPNN_EC_OK; } private bool RealUploadFile(Action callback, FileTokenType tokenType, byte mtype, byte[] fileContent, string filename, string fileExtension, string attrs, Dictionary rtmAttrs, int timeout) { if (mtype < 40 || mtype > 50) { if (errorRecorder != null) errorRecorder.RecordError("Send file require mtype between [40, 50], current mtype is " + mtype); if (RTMConfig.triggerCallbackIfAsyncMethodReturnFalse) ClientEngine.RunTask(() => { callback(null, 0, ErrorCode.RTM_EC_INVALID_MTYPE); }); return false; } TCPClient client = GetCoreClient(); if (client == null) { if (RTMConfig.triggerCallbackIfAsyncMethodReturnFalse) ClientEngine.RunTask(() => { callback(null, 0, fpnn.ErrorCode.FPNN_EC_CORE_INVALID_CONNECTION); }); return false; } SendFileInfo info = new SendFileInfo { actionType = tokenType, xid = 0, mtype = mtype, fileContent = fileContent, filename = filename, fileExtension = fileExtension, userAttrs = attrs, remainTimeout = timeout, lastActionTimestamp = ClientEngine.GetCurrentMilliseconds(), uploadCallback = callback }; info.rtmAttrs = rtmAttrs; bool asyncStarted = FileToken((string token, string endpoint, int errorCode) => { GetFileTokenUploadCallback(info, token, endpoint, errorCode); }, tokenType, info.xid, timeout); if (!asyncStarted && RTMConfig.triggerCallbackIfAsyncMethodReturnFalse) ClientEngine.RunTask(() => { callback(null, 0, fpnn.ErrorCode.FPNN_EC_CORE_INVALID_CONNECTION); }); return asyncStarted; } private int RealUploadFile(out string url, out uint size, FileTokenType tokenType, byte mtype, byte[] fileContent, string filename, string fileExtension, string attrs, Dictionary rtmAttrs, int timeout) { url = null; size = 0; //----------[ 1. check mtype ]---------------// if (mtype < 40 || mtype > 50) { if (errorRecorder != null) errorRecorder.RecordError("Send file require mtype between [40, 50], current mtype is " + mtype); return ErrorCode.RTM_EC_INVALID_MTYPE; } //----------[ 2. Get File Token ]---------------// TCPClient client = GetCoreClient(); if (client == null) return fpnn.ErrorCode.FPNN_EC_CORE_INVALID_CONNECTION; long lastActionTimestamp = ClientEngine.GetCurrentMilliseconds(); int errorCode = FileToken(out string token, out string endpoint, tokenType, 0, timeout); if (errorCode != fpnn.ErrorCode.FPNN_EC_OK) return errorCode; //----------[ 2.1 check timeout ]---------------// UpdateTimeout(ref timeout, ref lastActionTimestamp); if (timeout <= 0) return fpnn.ErrorCode.FPNN_EC_CORE_TIMEOUT; //----------[ 3. fetch file gate client ]---------------// TCPClient fileClient = RTMControlCenter.FecthFileGateClient(endpoint); if (fileClient == null) { fileClient = TCPClient.Create(endpoint, true); if (fileClient.SyncConnect()) { RTMControlCenter.ActiveFileGateClient(endpoint, fileClient); } else { //----------[ 3.1 check timeout ]---------------// UpdateTimeout(ref timeout, ref lastActionTimestamp); if (timeout <= 0) return fpnn.ErrorCode.FPNN_EC_CORE_TIMEOUT; if (ConvertIPv4EndpointToIPv6IPPort(endpoint, out string ipv6, out int port)) { fileClient = TCPClient.Create(ipv6 + ":" + port, true); if (fileClient.SyncConnect()) { RTMControlCenter.ActiveFileGateClient(endpoint, fileClient); } else { return fpnn.ErrorCode.FPNN_EC_CORE_INVALID_CONNECTION; } } else { return fpnn.ErrorCode.FPNN_EC_CORE_INVALID_CONNECTION; } } } //----------[ 3.2 check timeout ]---------------// UpdateTimeout(ref timeout, ref lastActionTimestamp); if (timeout <= 0) return fpnn.ErrorCode.FPNN_EC_CORE_TIMEOUT; //----------[ 4. build quest ]---------------// SendFileInfo info = new SendFileInfo { actionType = tokenType, xid = 0, mtype = mtype, fileContent = fileContent, filename = filename, fileExtension = fileExtension, userAttrs = attrs, token = token, }; info.rtmAttrs = rtmAttrs; long messageId = 0; Quest quest = BuildSendFileQuest(out messageId, info); Answer answer = fileClient.SendQuest(quest, timeout); if (answer.IsException()) return answer.ErrorCode(); url = answer.Want("url"); size = answer.Want("size"); RTMControlCenter.ActiveFileGateClient(endpoint, fileClient); // mtime = answer.Want("mtime"); return fpnn.ErrorCode.FPNN_EC_OK; } //===========================[ Send File ]=========================// public bool SendFile(MessageIdDelegate callback, long peerUid, MessageType type, byte[] fileContent, string filename, string fileExtension = "", string attrs = "", int timeout = 120) { return RealSendFile(callback, FileTokenType.P2P, peerUid, (byte)type, fileContent, filename, fileExtension, attrs, null, 0, timeout); } public int SendFile(out long messageId, long peerUid, MessageType type, byte[] fileContent, string filename, string fileExtension = "", string attrs = "", int timeout = 120) { return RealSendFile(out messageId, out _, FileTokenType.P2P, peerUid, (byte)type, fileContent, filename, fileExtension, attrs, null, 0, timeout); } public bool SendFile(SendMessageDelegate callback, long peerUid, MessageType type, byte[] fileContent, string filename, string fileExtension = "", string attrs = "", int timeout = 120) { return RealSendFile(callback, FileTokenType.P2P, peerUid, (byte)type, fileContent, filename, fileExtension, attrs, null, 0, timeout); } public int SendFile(out long messageId, out long mtime, long peerUid, MessageType type, byte[] fileContent, string filename, string fileExtension = "", string attrs = "", int timeout = 120) { return RealSendFile(out messageId, out mtime, FileTokenType.P2P, peerUid, (byte)type, fileContent, filename, fileExtension, attrs, null, 0, timeout); } //===========================[ Sned Group File ]=========================// public bool SendGroupFile(MessageIdDelegate callback, long groupId, MessageType type, byte[] fileContent, string filename, string fileExtension = "", string attrs = "", int timeout = 120) { return RealSendFile(callback, FileTokenType.Group, groupId, (byte)type, fileContent, filename, fileExtension, attrs, null, 0, timeout); } public int SendGroupFile(out long messageId, long groupId, MessageType type, byte[] fileContent, string filename, string fileExtension = "", string attrs = "", int timeout = 120) { return RealSendFile(out messageId, out _, FileTokenType.Group, groupId, (byte)type, fileContent, filename, fileExtension, attrs, null, 0, timeout); } public bool SendGroupFile(SendMessageDelegate callback, long groupId, MessageType type, byte[] fileContent, string filename, string fileExtension = "", string attrs = "", int timeout = 120) { return RealSendFile(callback, FileTokenType.Group, groupId, (byte)type, fileContent, filename, fileExtension, attrs, null, 0, timeout); } public int SendGroupFile(out long messageId, out long mtime, long groupId, MessageType type, byte[] fileContent, string filename, string fileExtension = "", string attrs = "", int timeout = 120) { return RealSendFile(out messageId, out mtime, FileTokenType.Group, groupId, (byte)type, fileContent, filename, fileExtension, attrs, null, 0, timeout); } //===========================[ Sned Room File ]=========================// public bool SendRoomFile(MessageIdDelegate callback, long roomId, MessageType type, byte[] fileContent, string filename, string fileExtension = "", string attrs = "", int timeout = 120) { return RealSendFile(callback, FileTokenType.Room, roomId, (byte)type, fileContent, filename, fileExtension, attrs, null, 0, timeout); } public int SendRoomFile(out long messageId, long roomId, MessageType type, byte[] fileContent, string filename, string fileExtension = "", string attrs = "", int timeout = 120) { return RealSendFile(out messageId, out _, FileTokenType.Room, roomId, (byte)type, fileContent, filename, fileExtension, attrs, null, 0, timeout); } public bool SendRoomFile(SendMessageDelegate callback, long roomId, MessageType type, byte[] fileContent, string filename, string fileExtension = "", string attrs = "", int timeout = 120) { return RealSendFile(callback, FileTokenType.Room, roomId, (byte)type, fileContent, filename, fileExtension, attrs, null, 0, timeout); } public int SendRoomFile(out long messageId, out long mtime, long roomId, MessageType type, byte[] fileContent, string filename, string fileExtension = "", string attrs = "", int timeout = 120) { return RealSendFile(out messageId, out mtime, FileTokenType.Room, roomId, (byte)type, fileContent, filename, fileExtension, attrs, null, 0, timeout); } //===========================[ Upload File ]=========================// public bool UploadFile(Action callback, MessageType type, byte[] fileContent, string filename, string fileExtension = "", string attrs = "", int timeout = 120) { return RealUploadFile(callback, FileTokenType.Upload, (byte)type, fileContent, filename, fileExtension, attrs, null, timeout); } public int UploadFile(out string url, out uint size, MessageType type, byte[] fileContent, string filename, string fileExtension = "", string attrs = "", int timeout = 120) { return RealUploadFile(out url, out size, FileTokenType.Upload, (byte)type, fileContent, filename, fileExtension, attrs, null, timeout); } } }