Beispiel #1
0
DWORD avatars_server_connection::sendUploadAvatarRequest(MCONTACT hContact, WORD wRef, const BYTE *data, size_t datalen)
{
	cookie_avatar *ack = (cookie_avatar*)SAFE_MALLOC(sizeof(cookie_avatar));
	if (!ack)
		return 0; // Failure: out of memory

	ack->hContact = hContact;

	DWORD dwCookie = ppro->AllocateCookie(CKT_AVATAR, ICQ_AVATAR_UPLOAD_REQUEST, 0, ack);

	icq_packet packet;
	serverPacketInit(&packet, 14 + datalen);
	packFNACHeader(&packet, ICQ_AVATAR_FAMILY, ICQ_AVATAR_UPLOAD_REQUEST, 0, dwCookie);
	packWord(&packet, wRef); // unknown, probably reference
	packWord(&packet, (WORD)datalen);
	packBuffer(&packet, data, datalen);

	if (sendServerPacket(&packet)) {
		ppro->debugLogA("Upload image packet sent.");
		return dwCookie;
	}

	ppro->ReleaseCookie(dwCookie); // failed to send, free resources
	return 0;
}
Beispiel #2
0
int icq_httpGatewayWrapSend(HANDLE hConn, PBYTE buf, int len, int flags, MIRANDASERVICE pfnNetlibSend)
{
	PBYTE sendBuf = buf;
	int sendLen = len;
	int sendResult = 0;

	while (sendLen > 0)
	{ // imitate polite behaviour of icq5.1 and split large packets
		icq_packet packet;
		WORD curLen;
		int curResult;

		if (sendLen > 512) curLen = 512; else curLen = (WORD)sendLen;
		// send wrapped data
		packet.wLen = curLen;
		write_httphdr(&packet, HTTP_PACKETTYPE_FLAP, GetGatewayIndex(hConn));
		packBuffer(&packet, sendBuf, (WORD)curLen);

		NETLIBBUFFER nlb={ (char*)packet.pData, packet.wLen, flags };
		curResult = pfnNetlibSend((WPARAM)hConn, (LPARAM)&nlb);
		
		SAFE_FREE((void**)&packet.pData);

		// sending failed, end loop
		if (curResult <= 0)
			return curResult;
		// calculare real number of data bytes sent
		if (curResult > 14) sendResult += curResult - 14;
		// move on
		sendLen -= curLen;
		sendBuf += curLen;
	}

	return sendResult;
}
Beispiel #3
0
int icq_httpGatewayBegin(HANDLE hConn, NETLIBOPENCONNECTION* nloc)
{ // open our "virual data connection"
	icq_packet packet;
	size_t serverNameLen;

	serverNameLen = strlennull(nloc->szHost);

	packet.wLen = (WORD)(serverNameLen + 4);
	write_httphdr(&packet, HTTP_PACKETTYPE_LOGIN, GetGatewayIndex(hConn));
	packWord(&packet, (WORD)serverNameLen);
	packBuffer(&packet, (LPBYTE)nloc->szHost, (WORD)serverNameLen);
	packWord(&packet, nloc->wPort);
	INT_PTR res = Netlib_Send(hConn, (char*)packet.pData, packet.wLen, MSG_DUMPPROXY|MSG_NOHTTPGATEWAYWRAP);
	SAFE_FREE((void**)&packet.pData);

	return res != SOCKET_ERROR;
}
static void file_sendData(CIcqProto* ppro, directconnect* dc)
{
	BYTE buf[2048];
	int bytesRead = 0;

	if (!dc->ft->currentIsDir)
	{
		icq_packet packet;

		if (dc->ft->fileId == -1)
			return;
		bytesRead = _read(dc->ft->fileId, buf, sizeof(buf));
		if (bytesRead == -1)
			return;

		directPacketInit(&packet, (WORD)(1 + bytesRead));
		packByte(&packet, PEER_FILE_DATA);   /* Ident */
		packBuffer(&packet, buf, (WORD)bytesRead);
		ppro->sendDirectPacket(dc, &packet);
	}

	dc->ft->dwBytesDone += bytesRead;
	dc->ft->dwFileBytesDone += bytesRead;

	if (GetTickCount() > dc->ft->dwLastNotify + 500 || bytesRead == 0)
	{
		PROTOFILETRANSFERSTATUS pfts;

		file_buildProtoFileTransferStatus(dc->ft, &pfts);
		ppro->BroadcastAck(dc->ft->hContact, ACKTYPE_FILE, ACKRESULT_DATA, dc->ft, (LPARAM)&pfts);

		dc->ft->dwLastNotify = GetTickCount();
	}

	if (bytesRead == 0)
	{
		if (!dc->ft->currentIsDir) _close(dc->ft->fileId);
		dc->ft->fileId = -1;
		dc->wantIdleTime = 0;
		dc->ft->iCurrentFile++;
		file_sendNextFile(ppro, dc);   /* this will close the socket if no more files */
	}
}
Beispiel #5
0
void CIcqProto::sendEntireListServ(WORD wFamily, WORD wSubtype, int listType)
{
	MCONTACT hResumeContact = NULL;

	do { // server doesn't seem to be able to cope with packets larger than 8k
		// send only about 100contacts per packet
		char *szList = buildUinList(listType, 0x3E8, &hResumeContact);

		size_t nListLen = mir_strlen(szList);
		if (nListLen) {
			icq_packet packet;
			serverPacketInit(&packet, nListLen + 10);
			packFNACHeader(&packet, wFamily, wSubtype);
			packBuffer(&packet, (LPBYTE)szList, nListLen);
			sendServPacket(&packet);
		}

		SAFE_FREE((void**)&szList);
	}
	while (hResumeContact);
}
static void file_sendNick(CIcqProto* ppro, directconnect* dc)
{
	icq_packet packet;
	char* szNick;
	WORD wNickLen;
  DBVARIANT dbv = {DBVT_DELETED};

	if (ppro->getSettingString(NULL, "Nick", &dbv))
		szNick = "";
	else
		szNick = dbv.pszVal;

	wNickLen = strlennull(szNick);

	directPacketInit(&packet, (WORD)(8 + wNickLen));
	packByte(&packet, PEER_FILE_INIT_ACK); /* Ident */
	packLEDWord(&packet, dc->ft->dwTransferSpeed);
	packLEWord(&packet, (WORD)(wNickLen + 1));
	packBuffer(&packet, (LPBYTE)szNick, (WORD)(wNickLen + 1));
	ppro->sendDirectPacket(dc, &packet);
	ICQFreeVariant(&dbv);
}
Beispiel #7
0
DWORD avatars_server_connection::sendGetAvatarRequest(MCONTACT hContact, DWORD dwUin, char *szUid, const BYTE *hash, size_t hashlen, const TCHAR *file)
{
	int i;
	DWORD dwNow = GetTickCount();

	mir_cslockfull alck(ppro->m_avatarsMutex);

	for (i = 0; i < runCount;) { // look for timeouted requests
		if (runTime[i] < dwNow) { // found outdated, remove
			runContact[i] = runContact[runCount - 1];
			runTime[i] = runTime[runCount - 1];
			runCount--;
		}
		else i++;
	}

	for (i = 0; i < runCount; i++) {
		if (runContact[i] == hContact) {
			ppro->debugLogA("Ignoring duplicate get %s image request.", strUID(dwUin, szUid));
			return -1; // Success: request ignored
		}
	}

	if (runCount < 4) { // 4 concurent requests at most
		int bSendNow = TRUE;
		{
			// rate management
			mir_cslock l(m_ratesMutex);
			WORD wGroup = m_rates->getGroupFromSNAC(ICQ_AVATAR_FAMILY, ICQ_AVATAR_GET_REQUEST);

			if (m_rates->getNextRateLevel(wGroup) < m_rates->getLimitLevel(wGroup, RML_ALERT)) { // we will be over quota if we send the request now, add to queue instead
				bSendNow = FALSE;
				ppro->debugLogA("Rates: Delay avatar request.");
			}
		}

		if (bSendNow) {
			runContact[runCount] = hContact;
			runTime[runCount] = GetTickCount() + 30000; // 30sec to complete request
			runCount++;

			alck.unlock();

			int nUinLen = getUIDLen(dwUin, szUid);

			cookie_avatar *ack = (cookie_avatar*)SAFE_MALLOC(sizeof(cookie_avatar));
			if (!ack)
				return 0; // Failure: out of memory

			ack->dwUin = 1; //dwUin; // I should be damned for this - only to identify get request
			ack->hContact = hContact;
			ack->hash = (BYTE*)SAFE_MALLOC(hashlen);
			memcpy(ack->hash, hash, hashlen); // copy the data
			ack->hashlen = hashlen;
			ack->szFile = null_strdup(file); // duplicate the string

			DWORD dwCookie = ppro->AllocateCookie(CKT_AVATAR, ICQ_AVATAR_GET_REQUEST, hContact, ack);
			icq_packet packet;

			serverPacketInit(&packet, 12 + nUinLen + hashlen);
			packFNACHeader(&packet, ICQ_AVATAR_FAMILY, ICQ_AVATAR_GET_REQUEST, 0, dwCookie);
			packUID(&packet, dwUin, szUid);
			packByte(&packet, 1); // unknown, probably type of request: 1 = get icon :)
			packBuffer(&packet, hash, hashlen);

			if (sendServerPacket(&packet)) {
				ppro->debugLogA("Request to get %s image sent.", strUID(dwUin, szUid));
				return dwCookie;
			}
			ppro->FreeCookie(dwCookie); // sending failed, free resources
			SAFE_FREE(&ack->szFile);
			SAFE_FREE((void**)&ack->hash);
			SAFE_FREE((void**)&ack);
		}
	}

	return 0; // Failure
}
Beispiel #8
0
void CIcqProto::handleServUINSettings(int nPort, serverthread_info *info)
{
	setUserInfo();

	/* SNAC 3,4: Tell server who's on our list (deprecated) */
	/* SNAC 3,15: Try to add unauthorised contacts to temporary list */
	sendEntireListServ(ICQ_BUDDY_FAMILY, ICQ_USER_ADDTOTEMPLIST, BUL_ALLCONTACTS);

	if (m_iDesiredStatus == ID_STATUS_INVISIBLE) {
		/* Tell server who's on our visible list (deprecated) */
		if (!m_bSsiEnabled)
			sendEntireListServ(ICQ_BOS_FAMILY, ICQ_CLI_ADDVISIBLE, BUL_VISIBLE);
		else
			updateServVisibilityCode(3);
	}

	if (m_iDesiredStatus != ID_STATUS_INVISIBLE) {
		/* Tell server who's on our invisible list (deprecated) */
		if (!m_bSsiEnabled)
			sendEntireListServ(ICQ_BOS_FAMILY, ICQ_CLI_ADDINVISIBLE, BUL_INVISIBLE);
		else
			updateServVisibilityCode(4);
	}

	// SNAC 1,1E: Set status
	icq_packet packet;
	{
		DWORD dwDirectCookie = rand() ^ (rand() << 16);

		// Get status
		WORD wStatus = MirandaStatusToIcq(m_iDesiredStatus);

		// Get status note & mood
		char *szStatusNote = PrepareStatusNote(m_iDesiredStatus);
		BYTE bXStatus = getContactXStatus(NULL);
		char szMoodData[32];

		// prepare mood id
		if (m_bMoodsEnabled && bXStatus && moodXStatus[bXStatus - 1] != -1)
			mir_snprintf(szMoodData, "icqmood%d", moodXStatus[bXStatus - 1]);
		else
			szMoodData[0] = '\0';

		//! Tricky code, this ensures that the status note will be saved to the directory
		SetStatusNote(szStatusNote, m_bGatewayMode ? 5000 : 2500, TRUE);

		size_t wStatusNoteLen = mir_strlen(szStatusNote);
		size_t wStatusMoodLen = mir_strlen(szMoodData);
		size_t wSessionDataLen = (wStatusNoteLen ? wStatusNoteLen + 4 : 0) + 4 + wStatusMoodLen + 4;

		serverPacketInit(&packet, 71 + (wSessionDataLen ? wSessionDataLen + 4 : 0));
		packFNACHeader(&packet, ICQ_SERVICE_FAMILY, ICQ_CLIENT_SET_STATUS);
		packDWord(&packet, 0x00060004);             // TLV 6: Status mode and security flags
		packWord(&packet, GetMyStatusFlags());      // Status flags
		packWord(&packet, wStatus);                 // Status
		packTLVWord(&packet, 0x0008, 0x0A06);       // TLV 8: Independent Status Messages
		packDWord(&packet, 0x000c0025);             // TLV C: Direct connection info
		packDWord(&packet, getDword("RealIP", 0));
		packDWord(&packet, nPort);
		packByte(&packet, DC_TYPE);                 // TCP/FLAG firewall settings
		packWord(&packet, ICQ_VERSION);
		packDWord(&packet, dwDirectCookie);         // DC Cookie
		packDWord(&packet, WEBFRONTPORT);           // Web front port
		packDWord(&packet, CLIENTFEATURES);         // Client features
		packDWord(&packet, 0x7fffffff);             // Abused timestamp
		packDWord(&packet, ICQ_PLUG_VERSION);       // Abused timestamp
		if (ServiceExists("SecureIM/IsContactSecured"))
			packDWord(&packet, 0x5AFEC0DE);           // SecureIM Abuse
		else
			packDWord(&packet, 0x00000000);           // Timestamp
		packWord(&packet, 0x0000);                  // Unknown
		packTLVWord(&packet, 0x001F, 0x0000);

		if (wSessionDataLen) { // Pack session data
			packWord(&packet, 0x1D);                  // TLV 1D
			packWord(&packet, WORD(wSessionDataLen));       // TLV length
			packWord(&packet, 0x02);                  // Item Type
			if (wStatusNoteLen) {
				packWord(&packet, 0x400 | WORD(wStatusNoteLen + 4)); // Flags + Item Length
				packWord(&packet, WORD(wStatusNoteLen));      // Text Length
				packBuffer(&packet, (LPBYTE)szStatusNote, wStatusNoteLen);
				packWord(&packet, 0);                   // Encoding not specified (utf-8 is default)
			}
			else
				packWord(&packet, 0);                   // Flags + Item Length
			packWord(&packet, 0x0E);                  // Item Type
			packWord(&packet, WORD(wStatusMoodLen));        // Flags + Item Length
			if (wStatusMoodLen)
				packBuffer(&packet, (LPBYTE)szMoodData, wStatusMoodLen); // Mood

			// Save current status note & mood
			db_set_utf(NULL, m_szModuleName, DBSETTING_STATUS_NOTE, szStatusNote);
			setString(DBSETTING_STATUS_MOOD, szMoodData);
		}
		// Release memory
		SAFE_FREE(&szStatusNote);

		sendServPacket(&packet);
	}

	/* SNAC 1,11 */
	serverPacketInit(&packet, 14);
	packFNACHeader(&packet, ICQ_SERVICE_FAMILY, ICQ_CLIENT_SET_IDLE);
	packDWord(&packet, 0x00000000);

	sendServPacket(&packet);
	m_bIdleAllow = 0;

	// Change status
	SetCurrentStatus(m_iDesiredStatus);

	// Finish Login sequence
	serverPacketInit(&packet, 98);
	packFNACHeader(&packet, ICQ_SERVICE_FAMILY, ICQ_CLIENT_READY);
	packDWord(&packet, 0x00220001); // imitate ICQ 6 behaviour
	packDWord(&packet, 0x0110164f);
	packDWord(&packet, 0x00010004);
	packDWord(&packet, 0x0110164f);
	packDWord(&packet, 0x00130004);
	packDWord(&packet, 0x0110164f);
	packDWord(&packet, 0x00020001);
	packDWord(&packet, 0x0110164f);
	packDWord(&packet, 0x00030001);
	packDWord(&packet, 0x0110164f);
	packDWord(&packet, 0x00150001);
	packDWord(&packet, 0x0110164f);
	packDWord(&packet, 0x00040001);
	packDWord(&packet, 0x0110164f);
	packDWord(&packet, 0x00060001);
	packDWord(&packet, 0x0110164f);
	packDWord(&packet, 0x00090001);
	packDWord(&packet, 0x0110164f);
	packDWord(&packet, 0x000A0001);
	packDWord(&packet, 0x0110164f);
	packDWord(&packet, 0x000B0001);
	packDWord(&packet, 0x0110164f);

	sendServPacket(&packet);

	debugLogA(" *** Yeehah, login sequence complete");

	// login sequence is complete enter logged-in mode
	info->bLoggedIn = true;
	m_bConnectionLost = false;

	// enable auto info-update routine
	icq_EnableUserLookup(true);

	if (!info->isMigrating) {
		// Get Offline Messages Reqeust
		cookie_offline_messages *ack = (cookie_offline_messages*)SAFE_MALLOC(sizeof(cookie_offline_messages));
		if (ack) {
			DWORD dwCookie = AllocateCookie(CKT_OFFLINEMESSAGE, ICQ_MSG_CLI_REQ_OFFLINE, 0, ack);

			serverPacketInit(&packet, 10);
			packFNACHeader(&packet, ICQ_MSG_FAMILY, ICQ_MSG_CLI_REQ_OFFLINE, 0, dwCookie);

			sendServPacket(&packet);
		}
		else icq_LogMessage(LOG_WARNING, LPGEN("Failed to request offline messages. They may be received next time you log in."));

		// Update our information from the server
		sendOwnerInfoRequest();

		// Request info updates on all contacts
		icq_RescanInfoUpdate();

		// Start sending Keep-Alive packets
		StartKeepAlive(info);

		if (m_bAvatarsEnabled) { // Send SNAC 1,4 - request avatar family 0x10 connection
			icq_requestnewfamily(ICQ_AVATAR_FAMILY, &CIcqProto::StartAvatarThread);

			m_avatarsConnectionPending = TRUE;
			debugLogA("Requesting Avatar family entry point.");
		}

		// Set last xstatus
		updateServerCustomStatus(TRUE);
	}
	info->isMigrating = false;

	if (m_bAimEnabled) {
		char **szAwayMsg = NULL;
		mir_cslock l(m_modeMsgsMutex);

		szAwayMsg = MirandaStatusToAwayMsg(m_iStatus);
		if (szAwayMsg)
			icq_sendSetAimAwayMsgServ(*szAwayMsg);
	}
}
Beispiel #9
0
// CLI_SETUSERINFO
void CIcqProto::setUserInfo()
{
	icq_packet packet;
	size_t wAdditionalData = 0;
	BYTE bXStatus = getContactXStatus(NULL);

	if (m_bAimEnabled)
		wAdditionalData += 16;
#ifdef DBG_CAPMTN
	wAdditionalData += 16;
#endif
	wAdditionalData += 16; // unicode
#ifdef DBG_NEWCAPS
	wAdditionalData += 16;
#endif
#ifdef DBG_CAPXTRAZ
	wAdditionalData += 16;
#endif
#ifdef DBG_OSCARFT
	wAdditionalData += 16;
#endif
	if (m_bAvatarsEnabled)
		wAdditionalData += 16;
	if (m_bXStatusEnabled && bXStatus != 0)
		wAdditionalData += 16;
#ifdef DBG_CAPHTML
	wAdditionalData += 16;
#endif
#ifdef DBG_AIMCONTACTSEND
	wAdditionalData += 16;
#endif

	wAdditionalData += CustomCapList.getCount() * 16;

	//MIM/PackName
	bool bHasPackName = false;
	DBVARIANT dbv;
	if (!db_get_s(NULL, "ICQCaps", "PackName", &dbv)) {
		//MIM/PackName
		bHasPackName = true;
		wAdditionalData += 16;
	}

	serverPacketInit(&packet, 62 + wAdditionalData);
	packFNACHeader(&packet, ICQ_LOCATION_FAMILY, ICQ_LOCATION_SET_USER_INFO);

	/* TLV(5): capability data */
	packWord(&packet, 0x0005);
	packWord(&packet, WORD(48 + wAdditionalData));

#ifdef DBG_CAPMTN
	packDWord(&packet, 0x563FC809); // CAP_TYPING
	packDWord(&packet, 0x0B6F41BD);
	packDWord(&packet, 0x9F794226);
	packDWord(&packet, 0x09DFA2F3);
#endif

	packShortCapability(&packet, 0x1349);  // AIM_CAPS_ICQSERVERRELAY

	// Broadcasts the capability to receive UTF8 encoded messages
	packShortCapability(&packet, 0x134E);  // CAP_UTF8MSGS

#ifdef DBG_NEWCAPS
	// Tells server we understand to new format of caps
	packShortCapability(&packet, 0x0000);  // CAP_SHORTCAPS
#endif

#ifdef DBG_CAPXTRAZ
	packDWord(&packet, 0x1a093c6c); // CAP_XTRAZ
	packDWord(&packet, 0xd7fd4ec5); // Broadcasts the capability to handle
	packDWord(&packet, 0x9d51a647); // Xtraz
	packDWord(&packet, 0x4e34f5a0);
#endif

	if (m_bAvatarsEnabled)
		packShortCapability(&packet, 0x134C);  // CAP_DEVILS

#ifdef DBG_OSCARFT
	// Broadcasts the capability to receive Oscar File Transfers
	packShortCapability(&packet, 0x1343);  // CAP_AIM_FILE
#endif

	// Tells the server we can speak to AIM
	if (m_bAimEnabled)
		packShortCapability(&packet, 0x134D);  // CAP_AIM_COMPATIBLE

#ifdef DBG_AIMCONTACTSEND
	packShortCapability(&packet, 0x134B);  // CAP_SENDBUDDYLIST
#endif

	if (m_bXStatusEnabled && bXStatus != 0)
		packBuffer(&packet, capXStatus[bXStatus - 1], BINARY_CAP_SIZE);

	packShortCapability(&packet, 0x1344);      // CAP_ICQDIRECT

#ifdef DBG_CAPHTML
	packShortCapability(&packet, 0x0002);      // CAP_HTMLMSGS
#endif

	packDWord(&packet, 0x4D697261);   // Miranda Signature
	packDWord(&packet, 0x6E64614E);

	WORD v[4];
	CallService(MS_SYSTEM_GETFILEVERSION, 0, (LPARAM)v);
	packWord(&packet, v[0]);
	packWord(&packet, v[1]);
	packWord(&packet, v[2]);
	packWord(&packet, v[3]);

	//MIM/PackName
	if (bHasPackName) {
		packBuffer(&packet, (BYTE*)dbv.pszVal, 0x10);
		db_free(&dbv);
	}

	if (CustomCapList.getCount())
		for (int i = 0; i < CustomCapList.getCount(); i++)
			packBuffer(&packet, (PBYTE)CustomCapList[i].caps, 0x10);

	sendServPacket(&packet);
}
static void file_sendNextFile(CIcqProto* ppro, directconnect* dc)
{
	icq_packet packet;
	struct _stati64 statbuf;
	char szThisSubDir[MAX_PATH];

	if (dc->ft->iCurrentFile >= (int)dc->ft->dwFileCount)
	{
		ppro->BroadcastAck(dc->ft->hContact, ACKTYPE_FILE, ACKRESULT_SUCCESS, dc->ft, 0);
		ppro->CloseDirectConnection(dc);
		dc->ft->hConnection = NULL;
		return;
	}

	dc->ft->szThisFile = dc->ft->pszFiles[dc->ft->iCurrentFile];
	if (FileStatUtf(dc->ft->szThisFile, &statbuf))
	{
		ppro->icq_LogMessage(LOG_ERROR, LPGEN("Your file transfer has been aborted because one of the files that you selected to send is no longer readable from the disk. You may have deleted or moved it."));
		ppro->CloseDirectConnection(dc);
		dc->ft->hConnection = NULL;
		return;
	}

	char *pszThisFileName = FindFilePathContainer((LPCSTR*)dc->ft->pszFiles, dc->ft->iCurrentFile, szThisSubDir);

	if (statbuf.st_mode&_S_IFDIR)
	{
		dc->ft->currentIsDir = 1;
	}
	else
	{
		dc->ft->currentIsDir = 0;
		dc->ft->fileId = OpenFileUtf(dc->ft->szThisFile, _O_BINARY | _O_RDONLY, _S_IREAD);
		if (dc->ft->fileId == -1)
		{
			ppro->icq_LogMessage(LOG_ERROR, LPGEN("Your file transfer has been aborted because one of the files that you selected to send is no longer readable from the disk. You may have deleted or moved it."));
			ppro->CloseDirectConnection(dc);
			dc->ft->hConnection = NULL;
			return;
		}

	}
	dc->ft->dwThisFileSize = statbuf.st_size;
	dc->ft->dwThisFileDate = statbuf.st_mtime;
	dc->ft->dwFileBytesDone = 0;

	char *szThisFileNameAnsi = NULL, *szThisSubDirAnsi = NULL;
	if (!utf8_decode(pszThisFileName, &szThisFileNameAnsi))
		szThisFileNameAnsi = NULL;
	if (!utf8_decode(szThisSubDir, &szThisSubDirAnsi))
		szThisSubDirAnsi = NULL;
	WORD wThisFileNameLen = strlennull(szThisFileNameAnsi);
	WORD wThisSubDirLen = strlennull(szThisSubDirAnsi);

	directPacketInit(&packet, (WORD)(20 + wThisFileNameLen + wThisSubDirLen));
	packByte(&packet, PEER_FILE_NEXTFILE); /* Ident */
	packByte(&packet, (BYTE)((statbuf.st_mode & _S_IFDIR) != 0)); // Is subdir
	packLEWord(&packet, (WORD)(wThisFileNameLen + 1));
	packBuffer(&packet, (LPBYTE)szThisFileNameAnsi, (WORD)(wThisFileNameLen + 1));
	packLEWord(&packet, (WORD)(wThisSubDirLen + 1));
	packBuffer(&packet, (LPBYTE)szThisSubDirAnsi, (WORD)(wThisSubDirLen + 1));
	packLEDWord(&packet, dc->ft->dwThisFileSize);
	packLEDWord(&packet, statbuf.st_mtime);
	packLEDWord(&packet, dc->ft->dwTransferSpeed);
	SAFE_FREE(&szThisFileNameAnsi);
	SAFE_FREE(&szThisSubDirAnsi);
	ppro->sendDirectPacket(dc, &packet);

	ppro->BroadcastAck(dc->ft->hContact, ACKTYPE_FILE, ACKRESULT_NEXTFILE, dc->ft, 0);
}