mirror of
https://github.com/Minecraft-Community-Edition/client.git
synced 2026-09-24 08:01:33 +00:00
Merge branch 'main' into aether-dimension
This commit is contained in:
@@ -57,6 +57,11 @@
|
||||
#include "..\Minecraft.World\GenericStats.h"
|
||||
#endif
|
||||
|
||||
#ifdef _WINDOWS64
|
||||
#include "Xbox\Network\NetworkPlayerXbox.h"
|
||||
#include "Common\Network\PlatformNetworkManagerStub.h"
|
||||
#endif
|
||||
|
||||
ClientConnection::ClientConnection(Minecraft *minecraft, const wstring& ip, int port)
|
||||
{
|
||||
// 4J Stu - No longer used as we use the socket version below.
|
||||
@@ -758,6 +763,27 @@ void ClientConnection::handleAddPlayer(shared_ptr<AddPlayerPacket> packet)
|
||||
player->displayName = player->name;
|
||||
#endif
|
||||
|
||||
#ifdef _WINDOWS64
|
||||
{
|
||||
PlayerUID pktXuid = player->getXuid();
|
||||
const PlayerUID WIN64_XUID_BASE = (PlayerUID)0xe000d45248242f2e;
|
||||
if (pktXuid >= WIN64_XUID_BASE && pktXuid < WIN64_XUID_BASE + MINECRAFT_NET_MAX_PLAYERS)
|
||||
{
|
||||
BYTE smallId = (BYTE)(pktXuid - WIN64_XUID_BASE);
|
||||
INetworkPlayer *np = g_NetworkManager.GetPlayerBySmallId(smallId);
|
||||
if (np != NULL)
|
||||
{
|
||||
NetworkPlayerXbox *npx = (NetworkPlayerXbox *)np;
|
||||
IQNetPlayer *qp = npx->GetQNetPlayer();
|
||||
if (qp != NULL && qp->m_gamertag[0] == 0)
|
||||
{
|
||||
wcsncpy_s(qp->m_gamertag, 32, packet->name.c_str(), _TRUNCATE);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// printf("\t\t\t\t%d: Add player\n",packet->id,packet->yRot);
|
||||
|
||||
int item = packet->carriedItem;
|
||||
@@ -893,6 +919,39 @@ void ClientConnection::handleMoveEntitySmall(shared_ptr<MoveEntityPacketSmall> p
|
||||
|
||||
void ClientConnection::handleRemoveEntity(shared_ptr<RemoveEntitiesPacket> packet)
|
||||
{
|
||||
#ifdef _WINDOWS64
|
||||
if (!g_NetworkManager.IsHost())
|
||||
{
|
||||
for (int i = 0; i < packet->ids.length; i++)
|
||||
{
|
||||
shared_ptr<Entity> entity = getEntity(packet->ids[i]);
|
||||
if (entity != NULL && entity->GetType() == eTYPE_PLAYER)
|
||||
{
|
||||
shared_ptr<Player> player = dynamic_pointer_cast<Player>(entity);
|
||||
if (player != NULL)
|
||||
{
|
||||
PlayerUID xuid = player->getXuid();
|
||||
INetworkPlayer *np = g_NetworkManager.GetPlayerByXuid(xuid);
|
||||
if (np != NULL)
|
||||
{
|
||||
NetworkPlayerXbox *npx = (NetworkPlayerXbox *)np;
|
||||
IQNetPlayer *qp = npx->GetQNetPlayer();
|
||||
if (qp != NULL)
|
||||
{
|
||||
extern CPlatformNetworkManagerStub *g_pPlatformNetworkManager;
|
||||
g_pPlatformNetworkManager->NotifyPlayerLeaving(qp);
|
||||
qp->m_smallId = 0;
|
||||
qp->m_isRemote = false;
|
||||
qp->m_isHostPlayer = false;
|
||||
qp->m_gamertag[0] = 0;
|
||||
qp->SetCustomDataValue(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
for (int i = 0; i < packet->ids.length; i++)
|
||||
{
|
||||
level->removeEntity(packet->ids[i]);
|
||||
|
||||
@@ -1220,12 +1220,17 @@ void CMinecraftApp::ActionGameSettings(int iPad,eGameSetting eVal)
|
||||
}
|
||||
break;
|
||||
case eGameSetting_Gamma:
|
||||
#ifdef _WINDOWS64
|
||||
pMinecraft->options->set(Options::Option::GAMMA, ((float)GameSettingsA[iPad]->ucGamma) / 100.0f);
|
||||
#else
|
||||
|
||||
if(iPad==ProfileManager.GetPrimaryPad())
|
||||
{
|
||||
// ucGamma range is 0-100, UpdateGamma is 0 - 32768
|
||||
float fVal=((float)GameSettingsA[iPad]->ucGamma)*327.68f;
|
||||
RenderManager.UpdateGamma((unsigned short)fVal);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
break;
|
||||
case eGameSetting_Difficulty:
|
||||
|
||||
@@ -40,10 +40,6 @@
|
||||
#include "..\Minecraft.World\DurangoStats.h"
|
||||
#endif
|
||||
|
||||
#ifdef _WINDOWS64
|
||||
#include "..\..\Windows64\Network\P2PConnectionManagerWin.h"
|
||||
#endif
|
||||
|
||||
// Global instance
|
||||
CGameNetworkManager g_NetworkManager;
|
||||
CPlatformNetworkManager *CGameNetworkManager::s_pPlatformNetworkManager;
|
||||
@@ -62,10 +58,6 @@ CGameNetworkManager::CGameNetworkManager()
|
||||
m_pUpsell = NULL;
|
||||
m_pInviteInfo = NULL;
|
||||
#endif
|
||||
|
||||
#ifdef _WINDOWS64
|
||||
m_p2pManager = NULL;
|
||||
#endif
|
||||
}
|
||||
|
||||
void CGameNetworkManager::Initialise()
|
||||
@@ -91,9 +83,6 @@ void CGameNetworkManager::Terminate()
|
||||
{
|
||||
if( m_bInitialised )
|
||||
{
|
||||
#ifdef _WINDOWS64
|
||||
TeardownP2PMesh();
|
||||
#endif
|
||||
s_pPlatformNetworkManager->Terminate();
|
||||
}
|
||||
}
|
||||
@@ -129,10 +118,6 @@ void CGameNetworkManager::DoWork()
|
||||
#endif
|
||||
s_pPlatformNetworkManager->DoWork();
|
||||
|
||||
#ifdef _WINDOWS64
|
||||
TickP2P();
|
||||
#endif
|
||||
|
||||
#ifdef __ORBIS__
|
||||
if (m_pUpsell != NULL && m_pUpsell->hasResponse())
|
||||
{
|
||||
@@ -181,16 +166,14 @@ bool CGameNetworkManager::_RunNetworkGame(LPVOID lpParameter)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Client needs QNET_STATE_GAME_PLAY so that IsInGameplay() returns true
|
||||
s_pPlatformNetworkManager->SetGamePlayState();
|
||||
}
|
||||
|
||||
if( g_NetworkManager.IsLeavingGame() ) return false;
|
||||
|
||||
#ifdef _WINDOWS64
|
||||
if (!g_NetworkManager.IsLocalGame())
|
||||
{
|
||||
g_NetworkManager.EstablishP2PMesh();
|
||||
}
|
||||
#endif
|
||||
|
||||
app.SetGameStarted(true);
|
||||
|
||||
// 4J-PB - if this is the trial game, start the trial timer
|
||||
@@ -1283,15 +1266,7 @@ bool CGameNetworkManager::SystemFlagGet(INetworkPlayer *pNetworkPlayer, int inde
|
||||
|
||||
wstring CGameNetworkManager::GatherStats()
|
||||
{
|
||||
wstring stats = s_pPlatformNetworkManager->GatherStats();
|
||||
#ifdef _WINDOWS64
|
||||
if (m_p2pManager != NULL)
|
||||
{
|
||||
stats += L"\n";
|
||||
stats += GatherP2PStats();
|
||||
}
|
||||
#endif
|
||||
return stats;
|
||||
return s_pPlatformNetworkManager->GatherStats();
|
||||
}
|
||||
|
||||
void CGameNetworkManager::renderQueueMeter()
|
||||
@@ -1421,7 +1396,10 @@ void CGameNetworkManager::CreateSocket( INetworkPlayer *pNetworkPlayer, bool loc
|
||||
Minecraft *pMinecraft = Minecraft::GetInstance();
|
||||
|
||||
Socket *socket = NULL;
|
||||
shared_ptr<MultiplayerLocalPlayer> mpPlayer = pMinecraft->localplayers[pNetworkPlayer->GetUserIndex()];
|
||||
shared_ptr<MultiplayerLocalPlayer> mpPlayer = nullptr;
|
||||
int userIdx = pNetworkPlayer->GetUserIndex();
|
||||
if (userIdx >= 0 && userIdx < XUSER_MAX_COUNT)
|
||||
mpPlayer = pMinecraft->localplayers[userIdx];
|
||||
if( localPlayer && mpPlayer != NULL && mpPlayer->connection != NULL)
|
||||
{
|
||||
// If we already have a MultiplayerLocalPlayer here then we are doing a session type change
|
||||
@@ -1518,7 +1496,7 @@ void CGameNetworkManager::PlayerJoining( INetworkPlayer *pNetworkPlayer )
|
||||
else
|
||||
{
|
||||
if( !pNetworkPlayer->IsHost() )
|
||||
{
|
||||
{
|
||||
for(int idx = 0; idx < XUSER_MAX_COUNT; ++idx)
|
||||
{
|
||||
if(Minecraft::GetInstance()->localplayers[idx] != NULL)
|
||||
@@ -1529,29 +1507,10 @@ void CGameNetworkManager::PlayerJoining( INetworkPlayer *pNetworkPlayer )
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef _WINDOWS64
|
||||
if (m_p2pManager != NULL && !pNetworkPlayer->IsLocal())
|
||||
{
|
||||
INetworkPlayer* localPlayer = GetLocalPlayerByUserIndex(0);
|
||||
if (localPlayer != NULL)
|
||||
{
|
||||
m_p2pManager->EstablishDirectConnection(localPlayer, pNetworkPlayer);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void CGameNetworkManager::PlayerLeaving( INetworkPlayer *pNetworkPlayer )
|
||||
{
|
||||
#ifdef _WINDOWS64
|
||||
// Disconnect P2P with the leaving player
|
||||
if (m_p2pManager != NULL && !pNetworkPlayer->IsLocal())
|
||||
{
|
||||
m_p2pManager->DisconnectPeer(pNetworkPlayer);
|
||||
}
|
||||
#endif
|
||||
|
||||
if( pNetworkPlayer->IsLocal() )
|
||||
{
|
||||
ProfileManager.SetCurrentGameActivity(pNetworkPlayer->GetUserIndex(),CONTEXT_PRESENCE_IDLE,false);
|
||||
@@ -2047,101 +2006,3 @@ void CGameNetworkManager::startAdhocMatching()
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef _WINDOWS64
|
||||
|
||||
///////////////////////////////////// P2P Networking /////////////////////////////////////
|
||||
|
||||
void CGameNetworkManager::InitializeP2PConnections()
|
||||
{
|
||||
if (m_p2pManager != NULL)
|
||||
return; // Already initialized
|
||||
|
||||
CP2PConnectionManagerWin* winP2P = new CP2PConnectionManagerWin();
|
||||
if (winP2P->Initialize())
|
||||
{
|
||||
m_p2pManager = winP2P;
|
||||
|
||||
// Wire up the P2P manager to the socket layer and packet router
|
||||
Socket::SetP2PManager(m_p2pManager);
|
||||
g_PacketRouter.Initialize(m_p2pManager);
|
||||
|
||||
app.DebugPrintf("P2P: Network manager initialized\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
delete winP2P;
|
||||
app.DebugPrintf("P2P: Failed to initialize network manager\n");
|
||||
}
|
||||
}
|
||||
|
||||
void CGameNetworkManager::EstablishP2PMesh()
|
||||
{
|
||||
if (m_p2pManager == NULL)
|
||||
InitializeP2PConnections();
|
||||
|
||||
if (m_p2pManager == NULL)
|
||||
return;
|
||||
|
||||
// Discover our public endpoint
|
||||
m_p2pManager->DiscoverPublicEndpoint();
|
||||
|
||||
INetworkPlayer* localPlayer = GetLocalPlayerByUserIndex(0);
|
||||
if (localPlayer == NULL)
|
||||
return;
|
||||
|
||||
int playerCount = GetPlayerCount();
|
||||
for (int i = 0; i < playerCount; i++)
|
||||
{
|
||||
INetworkPlayer* player = GetPlayerByIndex(i);
|
||||
if (player != NULL && !player->IsLocal() && player != localPlayer)
|
||||
{
|
||||
m_p2pManager->EstablishDirectConnection(localPlayer, player);
|
||||
}
|
||||
}
|
||||
|
||||
app.DebugPrintf("P2P: Mesh establishment initiated for %d players\n", playerCount);
|
||||
}
|
||||
|
||||
void CGameNetworkManager::TeardownP2PMesh()
|
||||
{
|
||||
if (m_p2pManager == NULL)
|
||||
return;
|
||||
|
||||
// Disconnect the P2P manager from socket layer and packet router
|
||||
Socket::SetP2PManager(NULL);
|
||||
g_PacketRouter.Shutdown();
|
||||
|
||||
// Shutdown and delete
|
||||
m_p2pManager->Shutdown();
|
||||
delete m_p2pManager;
|
||||
m_p2pManager = NULL;
|
||||
|
||||
app.DebugPrintf("P2P: Mesh torn down\n");
|
||||
}
|
||||
|
||||
void CGameNetworkManager::TickP2P()
|
||||
{
|
||||
if (m_p2pManager != NULL)
|
||||
{
|
||||
m_p2pManager->Tick();
|
||||
}
|
||||
}
|
||||
|
||||
EP2PConnectionState CGameNetworkManager::GetP2PConnectionState(INetworkPlayer* player)
|
||||
{
|
||||
if (m_p2pManager == NULL || player == NULL)
|
||||
return P2P_STATE_DISCONNECTED;
|
||||
|
||||
return m_p2pManager->GetConnectionState(player);
|
||||
}
|
||||
|
||||
wstring CGameNetworkManager::GatherP2PStats()
|
||||
{
|
||||
if (m_p2pManager == NULL)
|
||||
return L"P2P: Not active\n";
|
||||
|
||||
return m_p2pManager->GetDebugStats();
|
||||
}
|
||||
|
||||
#endif // _WINDOWS64
|
||||
|
||||
@@ -15,11 +15,6 @@ using namespace std;
|
||||
#endif
|
||||
#include "SessionInfo.h"
|
||||
|
||||
#ifdef _WINDOWS64
|
||||
#include "P2PConnectionManager.h"
|
||||
#include "PacketRouter.h"
|
||||
#endif
|
||||
|
||||
#ifdef __ORBIS__
|
||||
#include "..\..\Orbis\Network\PsPlusUpsellWrapper_Orbis.h"
|
||||
#endif
|
||||
@@ -164,17 +159,6 @@ public:
|
||||
void renderQueueMeter();
|
||||
wstring GatherRTTStats();
|
||||
|
||||
#ifdef _WINDOWS64
|
||||
void InitializeP2PConnections();
|
||||
void EstablishP2PMesh();
|
||||
void TeardownP2PMesh();
|
||||
void TickP2P();
|
||||
IP2PConnectionManager* GetP2PManager() { return m_p2pManager; }
|
||||
EP2PConnectionState GetP2PConnectionState(INetworkPlayer* player);
|
||||
bool IsP2PActive() { return m_p2pManager != NULL; }
|
||||
wstring GatherP2PStats();
|
||||
#endif
|
||||
|
||||
// GUI debug output
|
||||
|
||||
// Used for debugging output
|
||||
@@ -217,21 +201,20 @@ private:
|
||||
C4JThread::Event* m_hServerReadyEvent;
|
||||
bool m_bInitialised;
|
||||
|
||||
public:
|
||||
static CPlatformNetworkManager *s_pPlatformNetworkManager;
|
||||
|
||||
#ifdef _XBOX_ONE
|
||||
public:
|
||||
void SetFullSessionMessageOnNextSessionChange() { m_bFullSessionMessageOnNextSessionChange = true; }
|
||||
#endif
|
||||
private:
|
||||
float m_lastPlayerEventTimeStart; // For telemetry
|
||||
static CPlatformNetworkManager *s_pPlatformNetworkManager;
|
||||
|
||||
bool m_bNetworkThreadRunning;
|
||||
int GetJoiningReadyPercentage();
|
||||
bool m_bLastDisconnectWasLostRoomOnly;
|
||||
bool m_bFullSessionMessageOnNextSessionChange;
|
||||
|
||||
#ifdef _WINDOWS64
|
||||
IP2PConnectionManager* m_p2pManager;
|
||||
#endif
|
||||
#if defined __PS3__ || defined __PSVITA__ || defined __ORBIS__
|
||||
bool m_bSignedOutofPSN;
|
||||
#endif
|
||||
|
||||
@@ -1,105 +0,0 @@
|
||||
#include "stdafx.h"
|
||||
#include "PacketRouter.h"
|
||||
|
||||
PacketRouter g_PacketRouter;
|
||||
|
||||
PacketRouter::PacketRouter()
|
||||
{
|
||||
m_p2pManager = NULL;
|
||||
m_p2pEnabled = false;
|
||||
m_p2pRoutedCount = 0;
|
||||
m_hostRoutedCount = 0;
|
||||
}
|
||||
|
||||
void PacketRouter::Initialize(IP2PConnectionManager* p2pManager)
|
||||
{
|
||||
m_p2pManager = p2pManager;
|
||||
m_p2pEnabled = (p2pManager != NULL);
|
||||
}
|
||||
|
||||
void PacketRouter::Shutdown()
|
||||
{
|
||||
m_p2pManager = NULL;
|
||||
m_p2pEnabled = false;
|
||||
ResetStats();
|
||||
}
|
||||
|
||||
ERouteDecision PacketRouter::GetRouteDecision(shared_ptr<Packet> packet, INetworkPlayer* targetPlayer)
|
||||
{
|
||||
// If P2P is disabled or no manager, always use host
|
||||
if (!m_p2pEnabled || m_p2pManager == NULL || targetPlayer == NULL)
|
||||
return ROUTE_VIA_HOST;
|
||||
|
||||
// Local players don't need P2P
|
||||
if (targetPlayer->IsLocal())
|
||||
return ROUTE_VIA_HOST;
|
||||
|
||||
int packetId = packet->getId();
|
||||
EPacketRoutingMode routingMode = Packet::GetRoutingMode(packetId);
|
||||
|
||||
switch (routingMode)
|
||||
{
|
||||
case ROUTING_HOST_ONLY:
|
||||
return ROUTE_VIA_HOST;
|
||||
|
||||
case ROUTING_PREFER_DIRECT:
|
||||
// Use P2P if we have a direct connection to this player
|
||||
if (m_p2pManager->IsDirectConnectionAvailable(targetPlayer))
|
||||
return ROUTE_VIA_P2P;
|
||||
return ROUTE_VIA_HOST;
|
||||
|
||||
case ROUTING_DIRECT_ONLY:
|
||||
if (m_p2pManager->IsDirectConnectionAvailable(targetPlayer))
|
||||
return ROUTE_VIA_P2P;
|
||||
return ROUTE_VIA_HOST; // Fallback to host if no direct link
|
||||
|
||||
case ROUTING_BROADCAST:
|
||||
return ROUTE_VIA_HOST; // Broadcasts always go through host
|
||||
|
||||
default:
|
||||
return ROUTE_VIA_HOST;
|
||||
}
|
||||
}
|
||||
|
||||
bool PacketRouter::RoutePacket(shared_ptr<Packet> packet, INetworkPlayer* targetPlayer,
|
||||
const void* serializedData, int dataSize)
|
||||
{
|
||||
ERouteDecision decision = GetRouteDecision(packet, targetPlayer);
|
||||
|
||||
if (decision == ROUTE_VIA_P2P && m_p2pManager != NULL)
|
||||
{
|
||||
EP2PChannel channel = GetChannelForPacket(packet->getId());
|
||||
m_p2pManager->SendDirect(targetPlayer, serializedData, dataSize, channel);
|
||||
m_p2pRoutedCount++;
|
||||
return true;
|
||||
}
|
||||
|
||||
m_hostRoutedCount++;
|
||||
return false; // Caller should use existing host path
|
||||
}
|
||||
|
||||
EP2PChannel PacketRouter::GetChannelForPacket(int packetId)
|
||||
{
|
||||
// Movement packets
|
||||
if ((packetId >= 10 && packetId <= 13) || // MovePlayerPacket variants
|
||||
(packetId >= 30 && packetId <= 34) || // MoveEntityPacket variants
|
||||
(packetId >= 162 && packetId <= 165)) // MoveEntityPacketSmall variants
|
||||
{
|
||||
return P2P_CHANNEL_MOVEMENT;
|
||||
}
|
||||
|
||||
// Animation packets
|
||||
if (packetId == 18 || packetId == 35) // AnimatePacket, RotateHeadPacket
|
||||
{
|
||||
return P2P_CHANNEL_ANIMATION;
|
||||
}
|
||||
|
||||
// Effects/sounds
|
||||
if (packetId == 62) // LevelSoundPacket
|
||||
{
|
||||
return P2P_CHANNEL_EFFECTS;
|
||||
}
|
||||
|
||||
// Default
|
||||
return P2P_CHANNEL_MOVEMENT;
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
#pragma once
|
||||
#include "..\..\..\Minecraft.World\Packet.h"
|
||||
#include "P2PConnectionManager.h"
|
||||
#include "NetworkPlayerInterface.h"
|
||||
|
||||
// Packet routing decision
|
||||
enum ERouteDecision
|
||||
{
|
||||
ROUTE_VIA_HOST = 0, // Send through host TCP (existing path)
|
||||
ROUTE_VIA_P2P, // Send directly via P2P UDP
|
||||
ROUTE_VIA_BOTH // Send via both paths (redundancy for critical packets)
|
||||
};
|
||||
|
||||
class PacketRouter
|
||||
{
|
||||
public:
|
||||
PacketRouter();
|
||||
|
||||
// Initialize with P2P manager reference
|
||||
void Initialize(IP2PConnectionManager* p2pManager);
|
||||
void Shutdown();
|
||||
|
||||
// Determine how to route a packet to a specific player
|
||||
ERouteDecision GetRouteDecision(shared_ptr<Packet> packet, INetworkPlayer* targetPlayer);
|
||||
|
||||
// Route a serialized packet - returns true if sent via P2P
|
||||
bool RoutePacket(shared_ptr<Packet> packet, INetworkPlayer* targetPlayer,
|
||||
const void* serializedData, int dataSize);
|
||||
|
||||
// Enable/disable P2P routing (global toggle)
|
||||
void SetP2PEnabled(bool enabled) { m_p2pEnabled = enabled; }
|
||||
bool IsP2PEnabled() const { return m_p2pEnabled; }
|
||||
|
||||
// Map packet IDs to P2P channels
|
||||
static EP2PChannel GetChannelForPacket(int packetId);
|
||||
|
||||
// Stats
|
||||
int GetP2PRoutedCount() const { return m_p2pRoutedCount; }
|
||||
int GetHostRoutedCount() const { return m_hostRoutedCount; }
|
||||
void ResetStats() { m_p2pRoutedCount = 0; m_hostRoutedCount = 0; }
|
||||
|
||||
private:
|
||||
IP2PConnectionManager* m_p2pManager;
|
||||
bool m_p2pEnabled;
|
||||
int m_p2pRoutedCount;
|
||||
int m_hostRoutedCount;
|
||||
};
|
||||
|
||||
extern PacketRouter g_PacketRouter;
|
||||
@@ -71,6 +71,7 @@ public:
|
||||
virtual void HostGame(int localUsersMask, bool bOnlineGame, bool bIsPrivate, unsigned char publicSlots = MINECRAFT_NET_MAX_PLAYERS, unsigned char privateSlots = 0) = 0;
|
||||
virtual int JoinGame(FriendSessionInfo *searchResult, int dwLocalUsersMask, int dwPrimaryUserIndex ) = 0;
|
||||
virtual void CancelJoinGame() {};
|
||||
virtual void SetGamePlayState() {};
|
||||
virtual bool SetLocalGame(bool isLocal) = 0;
|
||||
virtual bool IsLocalGame() = 0;
|
||||
virtual void SetPrivateGame(bool isPrivate) = 0;
|
||||
|
||||
@@ -1,8 +1,14 @@
|
||||
#include "stdafx.h"
|
||||
#include "stdafx.h"
|
||||
#include "..\..\..\Minecraft.World\Socket.h"
|
||||
#include "..\..\..\Minecraft.World\StringHelpers.h"
|
||||
#include "PlatformNetworkManagerStub.h"
|
||||
#include "..\..\Xbox\Network\NetworkPlayerXbox.h" // TODO - stub version of this?
|
||||
#ifdef _WINDOWS64
|
||||
#include "..\..\Windows64\Network\WinsockNetLayer.h"
|
||||
#include "..\..\Minecraft.h"
|
||||
#include "..\..\User.h"
|
||||
#include "Extrax64Stubs.h"
|
||||
#endif
|
||||
|
||||
CPlatformNetworkManagerStub *g_pPlatformNetworkManager;
|
||||
|
||||
@@ -72,7 +78,7 @@ void CPlatformNetworkManagerStub::NotifyPlayerJoined(IQNetPlayer *pQNetPlayer )
|
||||
}
|
||||
}
|
||||
g_NetworkManager.PlayerJoining( networkPlayer );
|
||||
|
||||
|
||||
if( createFakeSocket == true && !m_bHostChanged )
|
||||
{
|
||||
g_NetworkManager.CreateSocket( networkPlayer, localPlayer );
|
||||
@@ -92,7 +98,7 @@ void CPlatformNetworkManagerStub::NotifyPlayerJoined(IQNetPlayer *pQNetPlayer )
|
||||
// g_NetworkManager.UpdateAndSetGameSessionData();
|
||||
SystemFlagAddPlayer( networkPlayer );
|
||||
}
|
||||
|
||||
|
||||
for( int idx = 0; idx < XUSER_MAX_COUNT; ++idx)
|
||||
{
|
||||
if(playerChangedCallback[idx] != NULL)
|
||||
@@ -114,10 +120,42 @@ void CPlatformNetworkManagerStub::NotifyPlayerJoined(IQNetPlayer *pQNetPlayer )
|
||||
}
|
||||
}
|
||||
|
||||
void CPlatformNetworkManagerStub::NotifyPlayerLeaving(IQNetPlayer *pQNetPlayer)
|
||||
{
|
||||
app.DebugPrintf("Player 0x%p \"%ls\" leaving.\n", pQNetPlayer, pQNetPlayer->GetGamertag());
|
||||
|
||||
INetworkPlayer *networkPlayer = getNetworkPlayer(pQNetPlayer);
|
||||
if (networkPlayer == NULL)
|
||||
return;
|
||||
|
||||
Socket *socket = networkPlayer->GetSocket();
|
||||
if (socket != NULL)
|
||||
{
|
||||
if (m_pIQNet->IsHost())
|
||||
g_NetworkManager.CloseConnection(networkPlayer);
|
||||
}
|
||||
|
||||
if (m_pIQNet->IsHost())
|
||||
{
|
||||
SystemFlagRemovePlayer(networkPlayer);
|
||||
}
|
||||
|
||||
g_NetworkManager.PlayerLeaving(networkPlayer);
|
||||
|
||||
for (int idx = 0; idx < XUSER_MAX_COUNT; ++idx)
|
||||
{
|
||||
if (playerChangedCallback[idx] != NULL)
|
||||
playerChangedCallback[idx](playerChangedCallbackParam[idx], networkPlayer, true);
|
||||
}
|
||||
|
||||
removeNetworkPlayer(pQNetPlayer);
|
||||
}
|
||||
|
||||
bool CPlatformNetworkManagerStub::Initialise(CGameNetworkManager *pGameNetworkManager, int flagIndexSize)
|
||||
{
|
||||
m_pGameNetworkManager = pGameNetworkManager;
|
||||
m_flagIndexSize = flagIndexSize;
|
||||
m_pIQNet = new IQNet();
|
||||
g_pPlatformNetworkManager = this;
|
||||
for( int i = 0; i < XUSER_MAX_COUNT; i++ )
|
||||
{
|
||||
@@ -148,19 +186,12 @@ bool CPlatformNetworkManagerStub::Initialise(CGameNetworkManager *pGameNetworkMa
|
||||
m_currentSearchResultsCount[i] = 0;
|
||||
}
|
||||
|
||||
#ifdef _WINDOWS64
|
||||
m_lanSessionManager.Initialize();
|
||||
#endif
|
||||
|
||||
// Success!
|
||||
return true;
|
||||
}
|
||||
|
||||
void CPlatformNetworkManagerStub::Terminate()
|
||||
{
|
||||
#ifdef _WINDOWS64
|
||||
m_lanSessionManager.Shutdown();
|
||||
#endif
|
||||
}
|
||||
|
||||
int CPlatformNetworkManagerStub::GetJoiningReadyPercentage()
|
||||
@@ -182,8 +213,61 @@ bool CPlatformNetworkManagerStub::isSystemPrimaryPlayer(IQNetPlayer *pQNetPlayer
|
||||
void CPlatformNetworkManagerStub::DoWork()
|
||||
{
|
||||
#ifdef _WINDOWS64
|
||||
m_lanSessionManager.Tick();
|
||||
TickSearch();
|
||||
extern QNET_STATE _iQNetStubState;
|
||||
if (_iQNetStubState == QNET_STATE_SESSION_STARTING && app.GetGameStarted())
|
||||
{
|
||||
_iQNetStubState = QNET_STATE_GAME_PLAY;
|
||||
if (m_pIQNet->IsHost())
|
||||
{
|
||||
WinsockNetLayer::UpdateAdvertiseJoinable(true);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
if (_iQNetStubState == QNET_STATE_IDLE)
|
||||
TickSearch();
|
||||
if (_iQNetStubState == QNET_STATE_GAME_PLAY && m_pIQNet->IsHost())
|
||||
{
|
||||
BYTE disconnectedSmallId;
|
||||
while (WinsockNetLayer::PopDisconnectedSmallId(&disconnectedSmallId))
|
||||
{
|
||||
IQNetPlayer *qnetPlayer = m_pIQNet->GetPlayerBySmallId(disconnectedSmallId);
|
||||
if (qnetPlayer != NULL && qnetPlayer->m_smallId == disconnectedSmallId)
|
||||
{
|
||||
NotifyPlayerLeaving(qnetPlayer);
|
||||
qnetPlayer->m_smallId = 0;
|
||||
qnetPlayer->m_isRemote = false;
|
||||
qnetPlayer->m_isHostPlayer = false;
|
||||
qnetPlayer->m_gamertag[0] = 0;
|
||||
qnetPlayer->SetCustomDataValue(0);
|
||||
WinsockNetLayer::PushFreeSmallId(disconnectedSmallId);
|
||||
if (IQNet::s_playerCount > 1)
|
||||
IQNet::s_playerCount--;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 1; i < MINECRAFT_NET_MAX_PLAYERS; i++)
|
||||
{
|
||||
IQNetPlayer *qp = &IQNet::m_player[i];
|
||||
if (qp->GetCustomDataValue() == 0)
|
||||
continue;
|
||||
INetworkPlayer *np = (INetworkPlayer *)qp->GetCustomDataValue();
|
||||
Socket *sock = np->GetSocket();
|
||||
if (sock != NULL && sock->isClosing())
|
||||
{
|
||||
WinsockNetLayer::CloseConnectionBySmallId((BYTE)i);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (_iQNetStubState == QNET_STATE_GAME_PLAY && !m_pIQNet->IsHost())
|
||||
{
|
||||
if (!WinsockNetLayer::IsConnected() && !g_NetworkManager.IsLeavingGame())
|
||||
{
|
||||
if (app.GetDisconnectReason() == DisconnectPacket::eDisconnect_None)
|
||||
app.SetDisconnectReason(DisconnectPacket::eDisconnect_Quitting);
|
||||
app.SetAction(ProfileManager.GetPrimaryPad(), eAppAction_ExitWorld, (void *)TRUE);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -244,16 +328,31 @@ bool CPlatformNetworkManagerStub::LeaveGame(bool bMigrateHost)
|
||||
m_bLeavingGame = true;
|
||||
|
||||
#ifdef _WINDOWS64
|
||||
m_lanSessionManager.StopBroadcasting();
|
||||
WinsockNetLayer::StopAdvertising();
|
||||
#endif
|
||||
|
||||
// If we are the host wait for the game server to end
|
||||
if(m_pIQNet->IsHost() && g_NetworkManager.ServerStoppedValid())
|
||||
{
|
||||
m_pIQNet->EndGame();
|
||||
g_NetworkManager.ServerStoppedWait();
|
||||
g_NetworkManager.ServerStoppedDestroy();
|
||||
}
|
||||
else
|
||||
{
|
||||
m_pIQNet->EndGame();
|
||||
}
|
||||
|
||||
for (AUTO_VAR(it, currentNetworkPlayers.begin()); it != currentNetworkPlayers.end(); it++)
|
||||
delete *it;
|
||||
currentNetworkPlayers.clear();
|
||||
m_machineQNetPrimaryPlayers.clear();
|
||||
SystemFlagReset();
|
||||
|
||||
#ifdef _WINDOWS64
|
||||
WinsockNetLayer::Shutdown();
|
||||
WinsockNetLayer::Initialize();
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -264,31 +363,38 @@ bool CPlatformNetworkManagerStub::_LeaveGame(bool bMigrateHost, bool bLeaveRoom)
|
||||
|
||||
void CPlatformNetworkManagerStub::HostGame(int localUsersMask, bool bOnlineGame, bool bIsPrivate, unsigned char publicSlots /*= MINECRAFT_NET_MAX_PLAYERS*/, unsigned char privateSlots /*= 0*/)
|
||||
{
|
||||
// #ifdef _XBOX
|
||||
// 4J Stu - We probably did this earlier as well, but just to be sure!
|
||||
SetLocalGame( !bOnlineGame );
|
||||
SetPrivateGame( bIsPrivate );
|
||||
SystemFlagReset();
|
||||
|
||||
// Make sure that the Primary Pad is in by default
|
||||
localUsersMask |= GetLocalPlayerMask( g_NetworkManager.GetPrimaryPad() );
|
||||
|
||||
m_bLeavingGame = false;
|
||||
|
||||
m_pIQNet->HostGame();
|
||||
|
||||
#ifdef _WINDOWS64
|
||||
IQNet::m_player[0].m_smallId = 0;
|
||||
IQNet::m_player[0].m_isRemote = false;
|
||||
IQNet::m_player[0].m_isHostPlayer = true;
|
||||
IQNet::s_playerCount = 1;
|
||||
#endif
|
||||
|
||||
_HostGame( localUsersMask, publicSlots, privateSlots );
|
||||
//#endif
|
||||
|
||||
#ifdef _WINDOWS64
|
||||
int port = WIN64_NET_DEFAULT_PORT;
|
||||
if (!WinsockNetLayer::IsActive())
|
||||
WinsockNetLayer::HostGame(port);
|
||||
|
||||
const wchar_t *hostName = IQNet::m_player[0].m_gamertag;
|
||||
unsigned int settings = app.GetGameHostOption(eGameHostOption_All);
|
||||
WinsockNetLayer::StartAdvertising(port, hostName, settings, 0, 0, MINECRAFT_NET_VERSION);
|
||||
#endif
|
||||
}
|
||||
|
||||
void CPlatformNetworkManagerStub::_HostGame(int usersMask, unsigned char publicSlots /*= MINECRAFT_NET_MAX_PLAYERS*/, unsigned char privateSlots /*= 0*/)
|
||||
{
|
||||
#ifdef _WINDOWS64
|
||||
// Start LAN broadcasting so other instances can discover this game
|
||||
char* gamertag = ProfileManager.GetGamertag(0);
|
||||
m_lanSessionManager.StartBroadcasting(m_hostGameSessionData, gamertag, "Minecraft World");
|
||||
app.DebugPrintf("LAN: Host game started, broadcasting session\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
bool CPlatformNetworkManagerStub::_StartGame()
|
||||
@@ -298,7 +404,52 @@ bool CPlatformNetworkManagerStub::_StartGame()
|
||||
|
||||
int CPlatformNetworkManagerStub::JoinGame(FriendSessionInfo *searchResult, int localUsersMask, int primaryUserIndex)
|
||||
{
|
||||
#ifdef _WINDOWS64
|
||||
if (searchResult == NULL)
|
||||
return CGameNetworkManager::JOINGAME_FAIL_GENERAL;
|
||||
|
||||
const char *hostIP = searchResult->data.hostIP;
|
||||
int hostPort = searchResult->data.hostPort;
|
||||
|
||||
if (hostPort <= 0 || hostIP[0] == 0)
|
||||
return CGameNetworkManager::JOINGAME_FAIL_GENERAL;
|
||||
|
||||
m_bLeavingGame = false;
|
||||
IQNet::s_isHosting = false;
|
||||
m_pIQNet->ClientJoinGame();
|
||||
|
||||
IQNet::m_player[0].m_smallId = 0;
|
||||
IQNet::m_player[0].m_isRemote = true;
|
||||
IQNet::m_player[0].m_isHostPlayer = true;
|
||||
wcsncpy_s(IQNet::m_player[0].m_gamertag, 32, searchResult->data.hostName, _TRUNCATE);
|
||||
|
||||
WinsockNetLayer::StopDiscovery();
|
||||
|
||||
if (!WinsockNetLayer::JoinGame(hostIP, hostPort))
|
||||
{
|
||||
app.DebugPrintf("Win64 LAN: Failed to connect to %s:%d\n", hostIP, hostPort);
|
||||
return CGameNetworkManager::JOINGAME_FAIL_GENERAL;
|
||||
}
|
||||
|
||||
BYTE localSmallId = WinsockNetLayer::GetLocalSmallId();
|
||||
|
||||
IQNet::m_player[localSmallId].m_smallId = localSmallId;
|
||||
IQNet::m_player[localSmallId].m_isRemote = false;
|
||||
IQNet::m_player[localSmallId].m_isHostPlayer = false;
|
||||
|
||||
Minecraft *pMinecraft = Minecraft::GetInstance();
|
||||
wcscpy_s(IQNet::m_player[localSmallId].m_gamertag, 32, pMinecraft->user->name.c_str());
|
||||
IQNet::s_playerCount = localSmallId + 1;
|
||||
|
||||
NotifyPlayerJoined(&IQNet::m_player[0]);
|
||||
NotifyPlayerJoined(&IQNet::m_player[localSmallId]);
|
||||
|
||||
m_pGameNetworkManager->StateChange_AnyToStarting();
|
||||
|
||||
return CGameNetworkManager::JOINGAME_SUCCESS;
|
||||
#else
|
||||
return CGameNetworkManager::JOINGAME_SUCCESS;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool CPlatformNetworkManagerStub::SetLocalGame(bool isLocal)
|
||||
@@ -331,54 +482,40 @@ void CPlatformNetworkManagerStub::UnRegisterPlayerChangedCallback(int iPad, void
|
||||
|
||||
void CPlatformNetworkManagerStub::HandleSignInChange()
|
||||
{
|
||||
return;
|
||||
return;
|
||||
}
|
||||
|
||||
void CPlatformNetworkManagerStub::SetGamePlayState()
|
||||
{
|
||||
#ifdef _WINDOWS64
|
||||
extern QNET_STATE _iQNetStubState;
|
||||
_iQNetStubState = QNET_STATE_GAME_PLAY;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool CPlatformNetworkManagerStub::_RunNetworkGame()
|
||||
{
|
||||
#ifdef _WINDOWS64
|
||||
extern QNET_STATE _iQNetStubState;
|
||||
_iQNetStubState = QNET_STATE_GAME_PLAY;
|
||||
|
||||
for (DWORD i = 0; i < IQNet::s_playerCount; i++)
|
||||
{
|
||||
if (IQNet::m_player[i].m_isRemote)
|
||||
{
|
||||
INetworkPlayer *pNetworkPlayer = getNetworkPlayer(&IQNet::m_player[i]);
|
||||
if (pNetworkPlayer != NULL && pNetworkPlayer->GetSocket() != NULL)
|
||||
{
|
||||
Socket::addIncomingSocket(pNetworkPlayer->GetSocket());
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
void CPlatformNetworkManagerStub::UpdateAndSetGameSessionData(INetworkPlayer *pNetworkPlayerLeaving /*= NULL*/)
|
||||
{
|
||||
// DWORD playerCount = m_pIQNet->GetPlayerCount();
|
||||
//
|
||||
// if( this->m_bLeavingGame )
|
||||
// return;
|
||||
//
|
||||
// if( GetHostPlayer() == NULL )
|
||||
// return;
|
||||
//
|
||||
// for(unsigned int i = 0; i < MINECRAFT_NET_MAX_PLAYERS; ++i)
|
||||
// {
|
||||
// if( i < playerCount )
|
||||
// {
|
||||
// INetworkPlayer *pNetworkPlayer = GetPlayerByIndex(i);
|
||||
//
|
||||
// // We can call this from NotifyPlayerLeaving but at that point the player is still considered in the session
|
||||
// if( pNetworkPlayer != pNetworkPlayerLeaving )
|
||||
// {
|
||||
// m_hostGameSessionData.players[i] = ((NetworkPlayerXbox *)pNetworkPlayer)->GetUID();
|
||||
//
|
||||
// char *temp;
|
||||
// temp = (char *)wstringtofilename( pNetworkPlayer->GetOnlineName() );
|
||||
// memcpy(m_hostGameSessionData.szPlayers[i],temp,XUSER_NAME_SIZE);
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// m_hostGameSessionData.players[i] = NULL;
|
||||
// memset(m_hostGameSessionData.szPlayers[i],0,XUSER_NAME_SIZE);
|
||||
// }
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// m_hostGameSessionData.players[i] = NULL;
|
||||
// memset(m_hostGameSessionData.szPlayers[i],0,XUSER_NAME_SIZE);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// m_hostGameSessionData.hostPlayerUID = ((NetworkPlayerXbox *)GetHostPlayer())->GetQNetPlayer()->GetXuid();
|
||||
// m_hostGameSessionData.m_uiGameHostSettings = app.GetGameHostOption(eGameHostOption_All);
|
||||
}
|
||||
|
||||
int CPlatformNetworkManagerStub::RemovePlayerOnSocketClosedThreadProc( void* lpParam )
|
||||
@@ -525,28 +662,59 @@ wstring CPlatformNetworkManagerStub::GatherRTTStats()
|
||||
void CPlatformNetworkManagerStub::TickSearch()
|
||||
{
|
||||
#ifdef _WINDOWS64
|
||||
// Start listening if we're not already and not hosting
|
||||
if (!m_lanSessionManager.IsListening() && !m_lanSessionManager.IsBroadcasting())
|
||||
{
|
||||
m_lanSessionManager.StartListening();
|
||||
}
|
||||
if (m_SessionsUpdatedCallback == NULL)
|
||||
return;
|
||||
|
||||
// If new sessions were discovered, notify the UI
|
||||
if (m_lanSessionManager.HasNewSessions())
|
||||
{
|
||||
m_lanSessionManager.ClearNewSessionsFlag();
|
||||
m_bSearchResultsReady = true;
|
||||
static DWORD lastSearchTime = 0;
|
||||
DWORD now = GetTickCount();
|
||||
if (now - lastSearchTime < 2000)
|
||||
return;
|
||||
lastSearchTime = now;
|
||||
|
||||
if (m_SessionsUpdatedCallback != NULL)
|
||||
{
|
||||
m_SessionsUpdatedCallback(m_pSearchParam);
|
||||
}
|
||||
}
|
||||
SearchForGames();
|
||||
#endif
|
||||
}
|
||||
|
||||
void CPlatformNetworkManagerStub::SearchForGames()
|
||||
{
|
||||
#ifdef _WINDOWS64
|
||||
std::vector<Win64LANSession> lanSessions = WinsockNetLayer::GetDiscoveredSessions();
|
||||
|
||||
for (size_t i = 0; i < friendsSessions[0].size(); i++)
|
||||
delete friendsSessions[0][i];
|
||||
friendsSessions[0].clear();
|
||||
|
||||
for (size_t i = 0; i < lanSessions.size(); i++)
|
||||
{
|
||||
FriendSessionInfo *info = new FriendSessionInfo();
|
||||
size_t nameLen = wcslen(lanSessions[i].hostName);
|
||||
info->displayLabel = new wchar_t[nameLen + 1];
|
||||
wcscpy_s(info->displayLabel, nameLen + 1, lanSessions[i].hostName);
|
||||
info->displayLabelLength = (unsigned char)nameLen;
|
||||
info->displayLabelViewableStartIndex = 0;
|
||||
|
||||
info->data.netVersion = lanSessions[i].netVersion;
|
||||
info->data.m_uiGameHostSettings = lanSessions[i].gameHostSettings;
|
||||
info->data.texturePackParentId = lanSessions[i].texturePackParentId;
|
||||
info->data.subTexturePackId = lanSessions[i].subTexturePackId;
|
||||
info->data.isReadyToJoin = lanSessions[i].isJoinable;
|
||||
info->data.isJoinable = lanSessions[i].isJoinable;
|
||||
strncpy_s(info->data.hostIP, sizeof(info->data.hostIP), lanSessions[i].hostIP, _TRUNCATE);
|
||||
info->data.hostPort = lanSessions[i].hostPort;
|
||||
wcsncpy_s(info->data.hostName, XUSER_NAME_SIZE, lanSessions[i].hostName, _TRUNCATE);
|
||||
info->data.playerCount = lanSessions[i].playerCount;
|
||||
info->data.maxPlayers = lanSessions[i].maxPlayers;
|
||||
|
||||
info->sessionId = (SessionID)((unsigned __int64)inet_addr(lanSessions[i].hostIP) | ((unsigned __int64)lanSessions[i].hostPort << 32));
|
||||
|
||||
friendsSessions[0].push_back(info);
|
||||
}
|
||||
|
||||
m_searchResultsCount[0] = (int)friendsSessions[0].size();
|
||||
|
||||
if (m_SessionsUpdatedCallback != NULL)
|
||||
m_SessionsUpdatedCallback(m_pSearchParam);
|
||||
#endif
|
||||
}
|
||||
|
||||
int CPlatformNetworkManagerStub::SearchForGamesThreadProc( void* lpParameter )
|
||||
@@ -562,12 +730,10 @@ void CPlatformNetworkManagerStub::SetSearchResultsReady(int resultCount)
|
||||
|
||||
vector<FriendSessionInfo *> *CPlatformNetworkManagerStub::GetSessionList(int iPad, int localPlayers, bool partyOnly)
|
||||
{
|
||||
#ifdef _WINDOWS64
|
||||
return m_lanSessionManager.GetSessionList();
|
||||
#else
|
||||
vector<FriendSessionInfo *> *filteredList = new vector<FriendSessionInfo *>();
|
||||
for (size_t i = 0; i < friendsSessions[0].size(); i++)
|
||||
filteredList->push_back(friendsSessions[0][i]);
|
||||
return filteredList;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool CPlatformNetworkManagerStub::GetGameSessionInfo(int iPad, SessionID sessionId, FriendSessionInfo *foundSessionInfo)
|
||||
@@ -596,15 +762,6 @@ void CPlatformNetworkManagerStub::ForceFriendsSessionRefresh()
|
||||
delete m_pSearchResults[i];
|
||||
m_pSearchResults[i] = NULL;
|
||||
}
|
||||
|
||||
#ifdef _WINDOWS64
|
||||
// Restart LAN listening to force a fresh discovery
|
||||
if (m_lanSessionManager.IsListening())
|
||||
{
|
||||
m_lanSessionManager.StopListening();
|
||||
m_lanSessionManager.StartListening();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
INetworkPlayer *CPlatformNetworkManagerStub::addNetworkPlayer(IQNetPlayer *pQNetPlayer)
|
||||
@@ -636,7 +793,7 @@ INetworkPlayer *CPlatformNetworkManagerStub::getNetworkPlayer(IQNetPlayer *pQNet
|
||||
|
||||
INetworkPlayer *CPlatformNetworkManagerStub::GetLocalPlayerByUserIndex(int userIndex )
|
||||
{
|
||||
return getNetworkPlayer(m_pIQNet->GetLocalPlayerByUserIndex(userIndex));
|
||||
return getNetworkPlayer(m_pIQNet->GetLocalPlayerByUserIndex(userIndex));
|
||||
}
|
||||
|
||||
INetworkPlayer *CPlatformNetworkManagerStub::GetPlayerByIndex(int playerIndex)
|
||||
@@ -651,7 +808,21 @@ INetworkPlayer * CPlatformNetworkManagerStub::GetPlayerByXuid(PlayerUID xuid)
|
||||
|
||||
INetworkPlayer * CPlatformNetworkManagerStub::GetPlayerBySmallId(unsigned char smallId)
|
||||
{
|
||||
return getNetworkPlayer(m_pIQNet->GetPlayerBySmallId(smallId));
|
||||
IQNetPlayer *qnetPlayer = m_pIQNet->GetPlayerBySmallId(smallId);
|
||||
if (qnetPlayer == NULL)
|
||||
return NULL;
|
||||
|
||||
INetworkPlayer *networkPlayer = getNetworkPlayer(qnetPlayer);
|
||||
#ifdef _WINDOWS64
|
||||
if (networkPlayer == NULL && smallId != 0 && !m_pIQNet->IsHost())
|
||||
{
|
||||
qnetPlayer->m_isRemote = true;
|
||||
qnetPlayer->m_isHostPlayer = false;
|
||||
NotifyPlayerJoined(qnetPlayer);
|
||||
networkPlayer = getNetworkPlayer(qnetPlayer);
|
||||
}
|
||||
#endif
|
||||
return networkPlayer;
|
||||
}
|
||||
|
||||
INetworkPlayer *CPlatformNetworkManagerStub::GetHostPlayer()
|
||||
|
||||
@@ -6,10 +6,6 @@ using namespace std;
|
||||
#include "PlatformNetworkManagerInterface.h"
|
||||
#include "SessionInfo.h"
|
||||
|
||||
#ifdef _WINDOWS64
|
||||
#include "LANSessionManager.h"
|
||||
#endif
|
||||
|
||||
class CPlatformNetworkManagerStub : public CPlatformNetworkManager
|
||||
{
|
||||
friend class CGameNetworkManager;
|
||||
@@ -59,15 +55,16 @@ public:
|
||||
virtual void HandleSignInChange();
|
||||
|
||||
virtual bool _RunNetworkGame();
|
||||
virtual void SetGamePlayState();
|
||||
|
||||
private:
|
||||
bool isSystemPrimaryPlayer(IQNetPlayer *pQNetPlayer);
|
||||
virtual bool _LeaveGame(bool bMigrateHost, bool bLeaveRoom);
|
||||
virtual void _HostGame(int dwUsersMask, unsigned char publicSlots = MINECRAFT_NET_MAX_PLAYERS, unsigned char privateSlots = 0);
|
||||
virtual bool _StartGame();
|
||||
|
||||
public:
|
||||
IQNet * m_pIQNet; // pointer to QNet interface
|
||||
|
||||
private:
|
||||
HANDLE m_notificationListener;
|
||||
|
||||
vector<IQNetPlayer *> m_machineQNetPrimaryPlayers; // collection of players that we deem to be the main one for that system
|
||||
@@ -165,16 +162,11 @@ public:
|
||||
virtual void GetFullFriendSessionInfo( FriendSessionInfo *foundSession, void (* FriendSessionUpdatedFn)(bool success, void *pParam), void *pParam );
|
||||
virtual void ForceFriendsSessionRefresh();
|
||||
|
||||
private:
|
||||
public:
|
||||
void NotifyPlayerJoined( IQNetPlayer *pQNetPlayer );
|
||||
void NotifyPlayerLeaving( IQNetPlayer *pQNetPlayer );
|
||||
|
||||
#ifndef _XBOX
|
||||
void FakeLocalPlayerJoined() { NotifyPlayerJoined(m_pIQNet->GetLocalPlayerByUserIndex(0)); }
|
||||
#endif
|
||||
|
||||
#ifdef _WINDOWS64
|
||||
CLANSessionManager* GetLANSessionManager() { return &m_lanSessionManager; }
|
||||
private:
|
||||
CLANSessionManager m_lanSessionManager;
|
||||
#endif
|
||||
};
|
||||
|
||||
@@ -63,12 +63,19 @@ typedef struct _GameSessionData
|
||||
#else
|
||||
typedef struct _GameSessionData
|
||||
{
|
||||
unsigned short netVersion; // 2 bytes
|
||||
unsigned int m_uiGameHostSettings; // 4 bytes
|
||||
unsigned int texturePackParentId; // 4 bytes
|
||||
unsigned char subTexturePackId; // 1 byte
|
||||
unsigned short netVersion;
|
||||
unsigned int m_uiGameHostSettings;
|
||||
unsigned int texturePackParentId;
|
||||
unsigned char subTexturePackId;
|
||||
|
||||
bool isReadyToJoin; // 1 byte
|
||||
bool isReadyToJoin;
|
||||
bool isJoinable;
|
||||
|
||||
char hostIP[64];
|
||||
int hostPort;
|
||||
wchar_t hostName[XUSER_NAME_SIZE];
|
||||
unsigned char playerCount;
|
||||
unsigned char maxPlayers;
|
||||
|
||||
_GameSessionData()
|
||||
{
|
||||
@@ -76,6 +83,13 @@ typedef struct _GameSessionData
|
||||
m_uiGameHostSettings = 0;
|
||||
texturePackParentId = 0;
|
||||
subTexturePackId = 0;
|
||||
isReadyToJoin = false;
|
||||
isJoinable = true;
|
||||
memset(hostIP, 0, sizeof(hostIP));
|
||||
hostPort = 0;
|
||||
memset(hostName, 0, sizeof(hostName));
|
||||
playerCount = 0;
|
||||
maxPlayers = MINECRAFT_NET_MAX_PLAYERS;
|
||||
}
|
||||
} GameSessionData;
|
||||
#endif
|
||||
|
||||
@@ -691,7 +691,7 @@ void UIController::tickInput()
|
||||
#endif
|
||||
{
|
||||
#ifdef _WINDOWS64
|
||||
if (!g_KBMInput.IsMouseGrabbed())
|
||||
if (!g_KBMInput.IsMouseGrabbed() && g_KBMInput.IsKBMActive())
|
||||
{
|
||||
UIScene *pScene = NULL;
|
||||
for (int grp = 0; grp < eUIGroup_COUNT && !pScene; ++grp)
|
||||
@@ -1018,7 +1018,7 @@ void UIController::handleKeyPress(unsigned int iPad, unsigned int key)
|
||||
released = InputManager.ButtonReleased(iPad,key); // Toggle
|
||||
|
||||
#ifdef _WINDOWS64
|
||||
if (iPad == 0)
|
||||
if (iPad == 0 && g_KBMInput.IsKBMActive())
|
||||
{
|
||||
int vk = 0;
|
||||
switch (key)
|
||||
@@ -1029,10 +1029,10 @@ void UIController::handleKeyPress(unsigned int iPad, unsigned int key)
|
||||
case ACTION_MENU_DOWN: vk = VK_DOWN; break;
|
||||
case ACTION_MENU_LEFT: vk = VK_LEFT; break;
|
||||
case ACTION_MENU_RIGHT: vk = VK_RIGHT; break;
|
||||
case ACTION_MENU_X: vk = 'E'; break;
|
||||
case ACTION_MENU_X: vk = 'R'; break;
|
||||
case ACTION_MENU_Y: vk = VK_TAB; break;
|
||||
case ACTION_MENU_LEFT_SCROLL: vk = 'Q'; break;
|
||||
case ACTION_MENU_RIGHT_SCROLL: vk = 'R'; break;
|
||||
case ACTION_MENU_RIGHT_SCROLL: vk = 'E'; break;
|
||||
case ACTION_MENU_PAGEUP: vk = VK_PRIOR; break;
|
||||
case ACTION_MENU_PAGEDOWN: vk = VK_NEXT; break;
|
||||
}
|
||||
@@ -1043,7 +1043,20 @@ void UIController::handleKeyPress(unsigned int iPad, unsigned int key)
|
||||
if (!pressed && !released && g_KBMInput.IsKeyDown(vk)) { down = true; }
|
||||
}
|
||||
|
||||
if ((key == ACTION_MENU_OK || key == ACTION_MENU_A) && !g_KBMInput.IsMouseGrabbed())
|
||||
if ((key == ACTION_MENU_UP || key == ACTION_MENU_DOWN) && !pressed && !released && !down)
|
||||
{
|
||||
bool inCrafting = (m_groups[(EUIGroup)(iPad+1)]->FindScene(eUIScene_Crafting2x2Menu) != NULL)
|
||||
|| (m_groups[(EUIGroup)(iPad+1)]->FindScene(eUIScene_Crafting3x3Menu) != NULL);
|
||||
if (inCrafting)
|
||||
{
|
||||
int wsKey = (key == ACTION_MENU_UP) ? 'W' : 'S';
|
||||
if (g_KBMInput.IsKeyPressed(wsKey)) { pressed = true; down = true; }
|
||||
if (g_KBMInput.IsKeyReleased(wsKey)) { released = true; down = false; }
|
||||
if (!pressed && !released && g_KBMInput.IsKeyDown(wsKey)) { down = true; }
|
||||
}
|
||||
}
|
||||
|
||||
if ((key == ACTION_MENU_OK || key == ACTION_MENU_A) && !g_KBMInput.IsMouseGrabbed() && g_KBMInput.IsKBMActive())
|
||||
{
|
||||
if (g_KBMInput.IsMouseButtonPressed(KeyboardMouseInput::MOUSE_LEFT)) { pressed = true; down = true; }
|
||||
if (g_KBMInput.IsMouseButtonReleased(KeyboardMouseInput::MOUSE_LEFT)) { released = true; down = false; }
|
||||
@@ -1477,7 +1490,7 @@ GDrawTexture * RADLINK UIController::TextureSubstitutionCreateCallback ( void *
|
||||
|
||||
// 4J Stu - All our flash controls that allow replacing textures use a special 64x64 symbol
|
||||
// Force this size here so that our images don't get scaled wildly
|
||||
#if (defined __ORBIS__ || defined _DURANGO )
|
||||
#if (defined __ORBIS__ || defined _DURANGO || defined _WINDOWS64 )
|
||||
*width = 96;
|
||||
*height = 96;
|
||||
#else
|
||||
|
||||
@@ -1164,7 +1164,7 @@ void UIScene::externalCallback(IggyExternalFunctionCallUTF16 * call)
|
||||
|
||||
void UIScene::registerSubstitutionTexture(const wstring &textureName, PBYTE pbData, DWORD dwLength, bool deleteData)
|
||||
{
|
||||
m_registeredTextures[textureName] = deleteData;;
|
||||
m_registeredTextures[textureName] = deleteData;
|
||||
ui.registerSubstitutionTexture(textureName, pbData, dwLength);
|
||||
}
|
||||
|
||||
|
||||
@@ -454,6 +454,11 @@ SCreditTextItemDef UIScene_Credits::gs_aCreditDefs[MAX_CREDIT_STRINGS] =
|
||||
{ L"Victoria Bruder (CompuCom Systems Inc)", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText },
|
||||
|
||||
#elif defined(_WIN64)
|
||||
{ L"", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, // extra blank line
|
||||
{ L"Code Used", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eExtraLargeText },
|
||||
{ L"", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, // extra blank line
|
||||
{ L"LCEMP Multiplayer", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eLargeText },
|
||||
{ L"notpies", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText },
|
||||
#elif defined(__PSVITA__)
|
||||
// font credits
|
||||
{ L"", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, // extra blank line
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#define PSVITA_CREDITS_COUNT 77
|
||||
#define PS4_CREDITS_COUNT 75
|
||||
#define XBOXONE_CREDITS_COUNT (75+318)
|
||||
#define WIN64_EXTRA_CREDITS_COUNT 5
|
||||
#define MILES_AND_IGGY_CREDITS_COUNT 8
|
||||
#define DYNAMODE_FONT_CREDITS_COUNT 2
|
||||
#define PS3_DOLBY_CREDIT 4
|
||||
@@ -15,8 +16,10 @@
|
||||
#define MAX_CREDIT_STRINGS (PS3_CREDITS_COUNT + MILES_AND_IGGY_CREDITS_COUNT + DYNAMODE_FONT_CREDITS_COUNT + PS3_DOLBY_CREDIT)
|
||||
#elif defined(__ORBIS__)
|
||||
#define MAX_CREDIT_STRINGS (PS4_CREDITS_COUNT + MILES_AND_IGGY_CREDITS_COUNT + DYNAMODE_FONT_CREDITS_COUNT)
|
||||
#elif defined(_DURANGO) || defined _WIN64
|
||||
#elif defined(_DURANGO)
|
||||
#define MAX_CREDIT_STRINGS (XBOXONE_CREDITS_COUNT + MILES_AND_IGGY_CREDITS_COUNT)
|
||||
#elif defined _WIN64
|
||||
#define MAX_CREDIT_STRINGS (XBOXONE_CREDITS_COUNT + WIN64_EXTRA_CREDITS_COUNT + MILES_AND_IGGY_CREDITS_COUNT)
|
||||
#elif defined(__PSVITA__)
|
||||
#define MAX_CREDIT_STRINGS (PSVITA_CREDITS_COUNT + MILES_AND_IGGY_CREDITS_COUNT + DYNAMODE_FONT_CREDITS_COUNT)
|
||||
#endif
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
// Windows64 DLC store stuff blahhhh - whisper
|
||||
#ifdef _WINDOWS64
|
||||
#include "Windows64_DLCOffers.h"
|
||||
#include "..\..\User.h"
|
||||
#include "Windows64_Minecraft.h"
|
||||
#include <unordered_map>
|
||||
static std::unordered_map<int,int> s_offerIndexToListPos;
|
||||
#endif
|
||||
@@ -166,10 +168,12 @@ void UIScene_DLCOffersMenu::handleInput(int iPad, int key, bool repeat, bool pre
|
||||
int iIndex = getControlChildFocus();
|
||||
if (iIndex >= 0 && iIndex < Windows64_DLCOffers::Get().GetOfferCount())
|
||||
{
|
||||
Minecraft *pMinecraft = Minecraft::GetInstance();
|
||||
|
||||
Windows64_DLCOffers::Get().InstallOffer(iIndex,
|
||||
[](const wchar_t* id, bool ok, void*) {
|
||||
wprintf(L"[DLC] Install %ls: %ls\n", id, ok ? L"OK" : L"FAILED");
|
||||
}, nullptr);
|
||||
}, nullptr, pMinecraft->user->name.c_str());
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -249,9 +253,9 @@ void UIScene_DLCOffersMenu::handlePress(F64 controlId, F64 childId)
|
||||
iIndex < Windows64_DLCOffers::Get().GetOfferCount())
|
||||
{
|
||||
Windows64_DLCOffers::Get().InstallOffer(iIndex,
|
||||
[](const wchar_t* id, bool ok, void*) {
|
||||
wprintf(L"[DLC] Install %ls: %ls\n", id, ok ? L"OK" : L"FAILED");
|
||||
}, nullptr);
|
||||
[](const wchar_t* id, bool ok, void*) {
|
||||
wprintf(L"[DLC] Install %ls: %ls\n", id, ok ? L"OK" : L"FAILED");
|
||||
}, nullptr, Minecraft::GetInstance()->user->name.c_str());
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
@@ -191,6 +191,8 @@ UIScene_LoadMenu::UIScene_LoadMenu(int iPad, void *initData, UILayer *parentLaye
|
||||
else
|
||||
{
|
||||
|
||||
|
||||
|
||||
#if defined(__PS3__) || defined(__ORBIS__)|| defined(_DURANGO) || defined (__PSVITA__)
|
||||
// convert to utf16
|
||||
uint16_t u16Message[MAX_SAVEFILENAME_LENGTH];
|
||||
|
||||
@@ -2,11 +2,6 @@
|
||||
#include "UI.h"
|
||||
#include "UIScene_LoadOrJoinMenu.h"
|
||||
|
||||
#ifdef _WINDOWS64
|
||||
#include "..\Network\P2PConnectionManager.h"
|
||||
#include "..\Network\GameNetworkManager.h"
|
||||
#endif
|
||||
|
||||
#include "..\..\..\Minecraft.World\StringHelpers.h"
|
||||
#include "..\..\..\Minecraft.World\net.minecraft.world.item.h"
|
||||
#include "..\..\..\Minecraft.World\net.minecraft.world.level.h"
|
||||
@@ -288,13 +283,8 @@ UIScene_LoadOrJoinMenu::~UIScene_LoadOrJoinMenu()
|
||||
g_NetworkManager.SetSessionsUpdatedCallback( NULL, NULL );
|
||||
app.SetLiveLinkRequired( false );
|
||||
|
||||
if(m_currentSessions)
|
||||
{
|
||||
for(AUTO_VAR(it, m_currentSessions->begin()); it < m_currentSessions->end(); ++it)
|
||||
{
|
||||
delete (*it);
|
||||
}
|
||||
}
|
||||
delete m_currentSessions;
|
||||
m_currentSessions = NULL;
|
||||
|
||||
#if TO_BE_IMPLEMENTED
|
||||
// Reset the background downloading, in case we changed it by attempting to download a texture pack
|
||||
@@ -1739,24 +1729,6 @@ void UIScene_LoadOrJoinMenu::UpdateGamesList()
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef _WINDOWS64
|
||||
if (g_NetworkManager.IsP2PActive() && g_NetworkManager.IsInGameplay())
|
||||
{
|
||||
int directPeers = g_NetworkManager.GetP2PManager()->GetDirectPeerCount();
|
||||
int totalOnline = g_NetworkManager.GetOnlinePlayerCount();
|
||||
if (directPeers > 0 && totalOnline > 1)
|
||||
{
|
||||
wchar_t labelWithP2P[256];
|
||||
swprintf_s(labelWithP2P, 256, L"%s [P2P:%d/%d]", sessionInfo->displayLabel, directPeers, totalOnline - 1);
|
||||
m_buttonListGames.addItem(labelWithP2P, textureName);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_buttonListGames.addItem(sessionInfo->displayLabel, textureName);
|
||||
}
|
||||
}
|
||||
else
|
||||
#endif
|
||||
m_buttonListGames.addItem( sessionInfo->displayLabel, textureName );
|
||||
|
||||
if(memcmp( &selectedSessionId, &sessionInfo->sessionId, sizeof(SessionID) ) == 0)
|
||||
|
||||
@@ -105,7 +105,6 @@ private:
|
||||
bool m_bSaveTransferCancelled;
|
||||
#endif
|
||||
bool m_bUpdateSaveSize;
|
||||
wstring m_directConnectAddress;
|
||||
|
||||
public:
|
||||
UIScene_LoadOrJoinMenu(int iPad, void *initData, UILayer *parentLayer);
|
||||
@@ -154,7 +153,6 @@ public:
|
||||
static int DeleteSaveDataReturned(LPVOID lpParam,bool bRes);
|
||||
static int RenameSaveDataReturned(LPVOID lpParam,bool bRes);
|
||||
static int KeyboardCompleteWorldNameCallback(LPVOID lpParam,bool bRes);
|
||||
static int KeyboardCompleteDirectConnectCallback(LPVOID lpParam,bool bRes);
|
||||
protected:
|
||||
void handlePress(F64 controlId, F64 childId);
|
||||
void LoadLevelGen(LevelGenerationOptions *levelGen);
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#ifdef _WINDOWS64
|
||||
#include "AchievementScreen.h"
|
||||
#include "StatsCounter.h"
|
||||
#include "Windows64_Minecraft.h"
|
||||
#endif
|
||||
#ifdef __ORBIS__
|
||||
#include <error_dialog.h>
|
||||
@@ -48,7 +49,7 @@ UIScene_MainMenu::UIScene_MainMenu(int iPad, void *initData, UILayer *parentLaye
|
||||
if(ProfileManager.IsFullVersion())
|
||||
{
|
||||
m_bTrialVersion=false;
|
||||
m_buttons[(int)eControl_UnlockOrDLC].init(app.GetString(IDS_DOWNLOADABLECONTENT),eControl_UnlockOrDLC);
|
||||
m_buttons[(int)eControl_UnlockOrDLC].init(L"LCE Workshop",eControl_UnlockOrDLC);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -115,6 +116,8 @@ UIScene_MainMenu::UIScene_MainMenu(int iPad, void *initData, UILayer *parentLaye
|
||||
// Fix for #45154 - Frontend: DLC: Content can only be downloaded from the frontend if you have not joined/exited multiplayer
|
||||
XBackgroundDownloadSetMode(XBACKGROUND_DOWNLOAD_MODE_ALWAYS_ALLOW);
|
||||
#endif
|
||||
|
||||
Minecraft::GetInstance()->user->name = convStringToWstring( ProfileManager.GetGamertag(ProfileManager.GetPrimaryPad()));
|
||||
}
|
||||
|
||||
UIScene_MainMenu::~UIScene_MainMenu()
|
||||
@@ -179,7 +182,7 @@ void UIScene_MainMenu::handleGainFocus(bool navBack)
|
||||
if(navBack && ProfileManager.IsFullVersion())
|
||||
{
|
||||
// Replace the Unlock Full Game with Downloadable Content
|
||||
m_buttons[(int)eControl_UnlockOrDLC].setLabel(app.GetString(IDS_DOWNLOADABLECONTENT));
|
||||
m_buttons[(int)eControl_UnlockOrDLC].setLabel(L"LCE Workshop");
|
||||
}
|
||||
|
||||
#if TO_BE_IMPLEMENTED
|
||||
@@ -368,6 +371,12 @@ void UIScene_MainMenu::handlePress(F64 controlId, F64 childId)
|
||||
break;
|
||||
#endif
|
||||
|
||||
#ifdef _WINDOWS64
|
||||
case eControl_Exit:
|
||||
PostMessage(GetMinecraftWindowHWND(), WM_CLOSE, 0, 0);
|
||||
break;
|
||||
#endif
|
||||
|
||||
default: __debugbreak();
|
||||
}
|
||||
|
||||
|
||||
@@ -75,12 +75,7 @@ void EnderDragonRenderer::renderModel(shared_ptr<Entity> _mob, float wp, float w
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
glColor4f(1, 0, 0, 0.5f);
|
||||
#ifdef __PSVITA__
|
||||
// AP - not sure that the usecompiled flag is supposed to be false. This makes it really slow on vita. Making it true still seems to look the same
|
||||
model->render(mob, wp, ws, bob, headRotMinusBodyRot, headRotx, scale, true);
|
||||
#else
|
||||
model->render(mob, wp, ws, bob, headRotMinusBodyRot, headRotx, scale, false);
|
||||
#endif
|
||||
glEnable(GL_TEXTURE_2D);
|
||||
glDisable(GL_BLEND);
|
||||
glDepthFunc(GL_LEQUAL);
|
||||
|
||||
+219
-278
@@ -1,4 +1,5 @@
|
||||
#include "stdafx.h"
|
||||
#include "Extrax64Stubs.h"
|
||||
#ifndef __PS3__
|
||||
//#include <compressapi.h>
|
||||
#endif // __PS3__
|
||||
@@ -40,6 +41,7 @@
|
||||
#ifdef _WINDOWS64
|
||||
#include "discord_game_sdk.h"
|
||||
#pragma comment(lib, "discord_game_sdk.dll.lib")
|
||||
#include "Windows64\Network\WinsockNetLayer.h"
|
||||
#endif
|
||||
|
||||
#ifndef E_FAIL
|
||||
@@ -72,6 +74,90 @@ VOID ATG::XMLParser::RegisterSAXCallbackInterface( ISAXCallback *pISAXCallback )
|
||||
static char s_discordUsername[32] = "";
|
||||
static ULONGLONG s_discordXuid = 0;
|
||||
static struct IDiscordCore *discordCore = NULL;
|
||||
static char s_DiscordJoinSecret[64] = "";
|
||||
static IDiscordActivityEvents s_ActivityEvents;
|
||||
|
||||
|
||||
void TickDiscord()
|
||||
{
|
||||
if(discordCore != NULL)
|
||||
discordCore->run_callbacks(discordCore);
|
||||
}
|
||||
|
||||
static char s_pendingJoinIP[64] = "";
|
||||
static int s_pendingJoinPort = 0;
|
||||
static bool s_hasPendingJoin = false;
|
||||
|
||||
|
||||
|
||||
static void OnActivityJoin(void* event_data, const char* join_secret)
|
||||
{
|
||||
printf("Discord OnActivityJoin: secret=%s\n", join_secret);
|
||||
|
||||
char secret[64];
|
||||
strncpy_s(secret, sizeof(secret), join_secret, _TRUNCATE);
|
||||
char* colon = strchr(secret, ':');
|
||||
if (colon != NULL)
|
||||
{
|
||||
*colon = '\0';
|
||||
strncpy_s(s_pendingJoinIP, sizeof(s_pendingJoinIP), secret, _TRUNCATE);
|
||||
s_pendingJoinPort = atoi(colon + 1);
|
||||
|
||||
FriendSessionInfo info;
|
||||
memset(&info, 0, sizeof(info));
|
||||
strncpy_s(info.data.hostIP, sizeof(info.data.hostIP), s_pendingJoinIP, _TRUNCATE);
|
||||
info.data.hostPort = s_pendingJoinPort;
|
||||
info.data.isJoinable = true;
|
||||
info.data.isReadyToJoin = true;
|
||||
|
||||
// Allocate displayLabel on heap so the destructor can safely free it
|
||||
static const wchar_t* label = L"Discord Invite";
|
||||
size_t labelLen = wcslen(label);
|
||||
info.displayLabel = new wchar_t[labelLen + 1];
|
||||
wcscpy_s(info.displayLabel, labelLen + 1, label);
|
||||
info.displayLabelLength = (unsigned char)labelLen;
|
||||
|
||||
|
||||
|
||||
g_NetworkManager.s_pPlatformNetworkManager->JoinGame(&info, 1,0);
|
||||
}
|
||||
}
|
||||
|
||||
static void OnActivityInvite(void* event_data, enum EDiscordActivityActionType type, struct DiscordUser* user, struct DiscordActivity* activity)
|
||||
{
|
||||
printf("Discord OnActivityInvite from user: %s\n", user->username);
|
||||
|
||||
struct IDiscordActivityManager* am = discordCore->get_activity_manager(discordCore);
|
||||
if (am != NULL)
|
||||
{
|
||||
static int s_dummy = 0;
|
||||
am->accept_invite(am, user->id, &s_dummy,
|
||||
[](void* data, enum EDiscordResult result)
|
||||
{
|
||||
printf("accept_invite result: %d\n", result);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
static void OnActivityJoinRequest(void* event_data, struct DiscordUser* user)
|
||||
{
|
||||
printf("Discord join request from: %s\n", user->username);
|
||||
|
||||
struct IDiscordActivityManager* am = discordCore->get_activity_manager(discordCore);
|
||||
if(am != NULL)
|
||||
{
|
||||
static int s_dummy = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void Discord_SetJoinSecret(const char* hostIp, WORD port)
|
||||
{
|
||||
printf("Discord_SetJoinSecret called!\n");
|
||||
sprintf_s(s_DiscordJoinSecret, sizeof(s_DiscordJoinSecret), "%s:%u", hostIp, (unsigned)port);
|
||||
|
||||
ProfileManager.SetCurrentGameActivity(0,4,false);
|
||||
}
|
||||
|
||||
|
||||
static void InitDiscordCore()
|
||||
{
|
||||
@@ -80,19 +166,31 @@ static void InitDiscordCore()
|
||||
static IDiscordCoreEvents coreEvents;
|
||||
memset(&coreEvents, 0, sizeof(coreEvents));
|
||||
|
||||
memset(&s_ActivityEvents, 0, sizeof(s_ActivityEvents));
|
||||
s_ActivityEvents.on_activity_join = OnActivityJoin;
|
||||
s_ActivityEvents.on_activity_invite = OnActivityInvite;
|
||||
s_ActivityEvents.on_activity_join_request = OnActivityJoinRequest;
|
||||
|
||||
|
||||
struct DiscordCreateParams params;
|
||||
DiscordCreateParamsSetDefault(¶ms);
|
||||
params.client_id = 1477855587978182828LL;
|
||||
params.flags = DiscordCreateFlags_NoRequireDiscord;
|
||||
params.events = &coreEvents;
|
||||
params.event_data = NULL;
|
||||
params.activity_events = &s_ActivityEvents;
|
||||
|
||||
enum EDiscordResult result = DiscordCreate(DISCORD_VERSION, ¶ms, &discordCore);
|
||||
if(result != DiscordResult_Ok)
|
||||
{
|
||||
printf("DiscordCreate failed: %d\n", result);
|
||||
discordCore = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
struct IDiscordActivityManager* am = discordCore->get_activity_manager(discordCore);
|
||||
if (am != NULL)
|
||||
am->register_command(am, "Minecraft.exe");
|
||||
}
|
||||
|
||||
static ULONGLONG DiscordIdToXuid(DiscordSnowflake discordId)
|
||||
@@ -108,9 +206,6 @@ static void InitDiscordIdentity()
|
||||
|
||||
for(int i = 0; i < 100; ++i)
|
||||
{
|
||||
discordCore->run_callbacks(discordCore);
|
||||
Sleep(10);
|
||||
|
||||
struct IDiscordUserManager *userManager = discordCore->get_user_manager(discordCore);
|
||||
if(userManager != NULL)
|
||||
{
|
||||
@@ -256,7 +351,7 @@ void PIXSetMarkerDeprecated(int a, char *b, ...) {}
|
||||
|
||||
bool IsEqualXUID(PlayerUID a, PlayerUID b)
|
||||
{
|
||||
#if defined(__PS3__) || defined(__ORBIS__) || defined (__PSVITA__) || defined(_DURANGO)
|
||||
#if defined(__PS3__) || defined(__ORBIS__) || defined (__PSVITA__) || defined(_DURANGO) || defined(_WINDOWS64)
|
||||
return (a == b);
|
||||
#else
|
||||
return false;
|
||||
@@ -280,33 +375,6 @@ namespace
|
||||
char s_stubGamertagA[XUSER_MAX_COUNT][32];
|
||||
wchar_t s_stubGamertagW[XUSER_MAX_COUNT][32];
|
||||
|
||||
DWORD StubSupportedLocalUserMask()
|
||||
{
|
||||
DWORD mask = 0;
|
||||
for(unsigned int i = 0; i < XUSER_MAX_COUNT && i < 32; ++i)
|
||||
{
|
||||
mask |= (1u << i);
|
||||
}
|
||||
return mask;
|
||||
}
|
||||
|
||||
bool StubSupportsLocalUser(DWORD userIndex)
|
||||
{
|
||||
return userIndex < XUSER_MAX_COUNT;
|
||||
}
|
||||
|
||||
DWORD StubClampLocalUserMask(DWORD usersMask)
|
||||
{
|
||||
DWORD clampedMask = usersMask & StubSupportedLocalUserMask();
|
||||
if(clampedMask == 0)
|
||||
{
|
||||
clampedMask = (1u << 0);
|
||||
}
|
||||
return clampedMask;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void EnsureStubIdentity()
|
||||
{
|
||||
if(s_stubIdentityInitialised)
|
||||
@@ -347,11 +415,7 @@ namespace
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifdef _WINDOWS64
|
||||
sprintf_s(s_stubGamertagA[i], sizeof(s_stubGamertagA[i]), "Player%u-%u", pid % 10000, i);
|
||||
#else
|
||||
snprintf(s_stubGamertagA[i], sizeof(s_stubGamertagA[i]), "Player%u-%u", pid % 10000, i);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@@ -365,70 +429,36 @@ namespace
|
||||
}
|
||||
}
|
||||
|
||||
extern bool _bQNetStubIsHost;
|
||||
BYTE IQNetPlayer::GetSmallId()
|
||||
{
|
||||
int idx = (int)(this - &IQNet::m_player[0]);
|
||||
if(idx < 0 || idx >= (int)XUSER_MAX_COUNT)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
if(!_bQNetStubIsHost)
|
||||
{
|
||||
// When acting as a client, reserve small id 0 for the remote host.
|
||||
return (BYTE)(idx + 1);
|
||||
}
|
||||
return (BYTE)idx;
|
||||
}
|
||||
QNET_STATE _iQNetStubState = QNET_STATE_IDLE;
|
||||
|
||||
BYTE IQNetPlayer::GetSmallId() { return m_smallId; }
|
||||
void IQNetPlayer::SendData(IQNetPlayer *player, const void *pvData, DWORD dwDataSize, DWORD dwFlags)
|
||||
{
|
||||
app.DebugPrintf("Sending from 0x%x to 0x%x %d bytes\n",this,player,dwDataSize);
|
||||
}
|
||||
bool IQNetPlayer::IsSameSystem(IQNetPlayer *player)
|
||||
{
|
||||
if(player == NULL)
|
||||
if (WinsockNetLayer::IsActive())
|
||||
{
|
||||
return false;
|
||||
WinsockNetLayer::SendToSmallId(player->m_smallId, pvData, dwDataSize);
|
||||
}
|
||||
|
||||
ULONG_PTR ptr = (ULONG_PTR)player;
|
||||
ULONG_PTR begin = (ULONG_PTR)&IQNet::m_player[0];
|
||||
ULONG_PTR end = (ULONG_PTR)&IQNet::m_player[XUSER_MAX_COUNT];
|
||||
return (ptr >= begin && ptr < end);
|
||||
}
|
||||
bool IQNetPlayer::IsSameSystem(IQNetPlayer *player) { return (this == player) || (!m_isRemote && !player->m_isRemote); }
|
||||
DWORD IQNetPlayer::GetSendQueueSize( IQNetPlayer *player, DWORD dwFlags ) { return 0; }
|
||||
DWORD IQNetPlayer::GetCurrentRtt() { return 0; }
|
||||
extern bool _bQNetStubIsHost;
|
||||
bool IQNetPlayer::IsHost() { return _bQNetStubIsHost && this == &IQNet::m_player[0]; }
|
||||
bool IQNetPlayer::IsHost() { return m_isHostPlayer; }
|
||||
bool IQNetPlayer::IsGuest() { return false; }
|
||||
bool IQNetPlayer::IsLocal() { return true; }
|
||||
bool IQNetPlayer::IsLocal() { return !m_isRemote; }
|
||||
PlayerUID IQNetPlayer::GetXuid()
|
||||
{
|
||||
EnsureStubIdentity();
|
||||
int idx = (int)(this - &IQNet::m_player[0]);
|
||||
if(idx < 0 || idx >= (int)XUSER_MAX_COUNT)
|
||||
// For the local player (smallId 0 on host, or the assigned smallId on client),
|
||||
// try Discord XUID first for richer identity
|
||||
if (!m_isRemote)
|
||||
{
|
||||
idx = 0;
|
||||
EnsureStubIdentity();
|
||||
if (s_discordXuid != 0)
|
||||
return s_discordXuid | 0x100000000ULL;
|
||||
}
|
||||
#if defined(__PS3__) || defined(__ORBIS__) || defined(__PSVITA__)
|
||||
PlayerUID uid;
|
||||
uid.setUserID(0x70000000u | (unsigned int)(idx & 0xFF));
|
||||
return uid;
|
||||
#else
|
||||
return (s_stubXuidBase + (ULONGLONG)(idx & 0x0F)) | 0x100000000ULL;
|
||||
#endif
|
||||
return (PlayerUID)(0xe000d45248242f2e + m_smallId);
|
||||
}
|
||||
LPCWSTR IQNetPlayer::GetGamertag()
|
||||
{
|
||||
EnsureStubIdentity();
|
||||
int idx = (int)(this - &IQNet::m_player[0]);
|
||||
if(idx < 0 || idx >= (int)XUSER_MAX_COUNT)
|
||||
{
|
||||
idx = 0;
|
||||
}
|
||||
return s_stubGamertagW[idx];
|
||||
}
|
||||
int IQNetPlayer::GetSessionIndex() { return GetSmallId(); }
|
||||
LPCWSTR IQNetPlayer::GetGamertag() { return m_gamertag; }
|
||||
int IQNetPlayer::GetSessionIndex() { return m_smallId; }
|
||||
bool IQNetPlayer::IsTalking() { return false; }
|
||||
bool IQNetPlayer::IsMutedByLocalUser(DWORD dwUserIndex) { return false; }
|
||||
bool IQNetPlayer::HasVoice() { return false; }
|
||||
@@ -441,156 +471,118 @@ ULONG_PTR IQNetPlayer::GetCustomDataValue() {
|
||||
return m_customData;
|
||||
}
|
||||
|
||||
IQNetPlayer IQNet::m_player[4];
|
||||
IQNetPlayer IQNet::m_player[MINECRAFT_NET_MAX_PLAYERS];
|
||||
DWORD IQNet::s_playerCount = 1;
|
||||
bool IQNet::s_isHosting = true;
|
||||
|
||||
bool _bQNetStubGameRunning = false;
|
||||
bool _bQNetStubIsHost = true;
|
||||
DWORD _dwQNetStubLocalUsersMask = (1u << 0);
|
||||
|
||||
HRESULT IQNet::AddLocalPlayerByUserIndex(DWORD dwUserIndex)
|
||||
void Win64_SetupRemoteQNetPlayer(IQNetPlayer *player, BYTE smallId, bool isHost, bool isLocal)
|
||||
{
|
||||
if(!StubSupportsLocalUser(dwUserIndex))
|
||||
{
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
_dwQNetStubLocalUsersMask |= (1u << dwUserIndex);
|
||||
return S_OK;
|
||||
player->m_smallId = smallId;
|
||||
player->m_isRemote = !isLocal;
|
||||
player->m_isHostPlayer = isHost;
|
||||
swprintf_s(player->m_gamertag, 32, L"Player%d", smallId);
|
||||
if (smallId >= IQNet::s_playerCount)
|
||||
IQNet::s_playerCount = smallId + 1;
|
||||
}
|
||||
|
||||
static bool Win64_IsActivePlayer(IQNetPlayer *p, DWORD index)
|
||||
{
|
||||
if (index == 0) return true;
|
||||
return (p->GetCustomDataValue() != 0);
|
||||
}
|
||||
|
||||
HRESULT IQNet::AddLocalPlayerByUserIndex(DWORD dwUserIndex){ return S_OK; }
|
||||
IQNetPlayer *IQNet::GetHostPlayer() { return &m_player[0]; }
|
||||
IQNetPlayer *IQNet::GetLocalPlayerByUserIndex(DWORD dwUserIndex)
|
||||
{
|
||||
if(!StubSupportsLocalUser(dwUserIndex))
|
||||
if (s_isHosting)
|
||||
{
|
||||
if (dwUserIndex < MINECRAFT_NET_MAX_PLAYERS &&
|
||||
!m_player[dwUserIndex].m_isRemote &&
|
||||
Win64_IsActivePlayer(&m_player[dwUserIndex], dwUserIndex))
|
||||
return &m_player[dwUserIndex];
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return (_dwQNetStubLocalUsersMask & (1u << dwUserIndex)) ? &m_player[dwUserIndex] : NULL;
|
||||
if (dwUserIndex != 0)
|
||||
return NULL;
|
||||
for (DWORD i = 0; i < s_playerCount; i++)
|
||||
{
|
||||
if (!m_player[i].m_isRemote && Win64_IsActivePlayer(&m_player[i], i))
|
||||
return &m_player[i];
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
IQNetPlayer *IQNet::GetPlayerByIndex(DWORD dwPlayerIndex)
|
||||
{
|
||||
if(!_bQNetStubGameRunning)
|
||||
DWORD found = 0;
|
||||
for (DWORD i = 0; i < s_playerCount; i++)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
DWORD currentIndex = 0;
|
||||
for(DWORD localUser = 0; localUser < XUSER_MAX_COUNT; ++localUser)
|
||||
{
|
||||
if((_dwQNetStubLocalUsersMask & (1u << localUser)) == 0)
|
||||
if (Win64_IsActivePlayer(&m_player[i], i))
|
||||
{
|
||||
continue;
|
||||
if (found == dwPlayerIndex) return &m_player[i];
|
||||
found++;
|
||||
}
|
||||
|
||||
if(currentIndex == dwPlayerIndex)
|
||||
{
|
||||
return &m_player[localUser];
|
||||
}
|
||||
|
||||
++currentIndex;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
return &m_player[0];
|
||||
}
|
||||
IQNetPlayer *IQNet::GetPlayerBySmallId(BYTE SmallId)
|
||||
{
|
||||
if(!_bQNetStubGameRunning)
|
||||
{
|
||||
if (SmallId >= MINECRAFT_NET_MAX_PLAYERS)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for(DWORD localUser = 0; localUser < XUSER_MAX_COUNT; ++localUser)
|
||||
{
|
||||
if((_dwQNetStubLocalUsersMask & (1u << localUser)) == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if(m_player[localUser].GetSmallId() == SmallId)
|
||||
{
|
||||
return &m_player[localUser];
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
m_player[SmallId].m_smallId = SmallId;
|
||||
if (SmallId >= s_playerCount)
|
||||
s_playerCount = SmallId + 1;
|
||||
return &m_player[SmallId];
|
||||
}
|
||||
IQNetPlayer *IQNet::GetPlayerByXuid(PlayerUID xuid)
|
||||
{
|
||||
if(!_bQNetStubGameRunning)
|
||||
for (DWORD i = 0; i < MINECRAFT_NET_MAX_PLAYERS; i++)
|
||||
{
|
||||
return NULL;
|
||||
if (Win64_IsActivePlayer(&m_player[i], i) && m_player[i].GetXuid() == xuid) return &m_player[i];
|
||||
}
|
||||
|
||||
for(DWORD localUser = 0; localUser < XUSER_MAX_COUNT; ++localUser)
|
||||
{
|
||||
if((_dwQNetStubLocalUsersMask & (1u << localUser)) == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
IQNetPlayer *localPlayer = &m_player[localUser];
|
||||
PlayerUID localXuidOnline = localPlayer->GetXuid();
|
||||
#if defined(__PS3__) || defined(__ORBIS__) || defined(__PSVITA__) || defined(_DURANGO)
|
||||
if(xuid == localXuidOnline)
|
||||
{
|
||||
return localPlayer;
|
||||
}
|
||||
#else
|
||||
PlayerUID localXuidOffline = localXuidOnline & ~0x100000000ULL;
|
||||
if(xuid == localXuidOnline || xuid == localXuidOffline)
|
||||
{
|
||||
return localPlayer;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
DWORD IQNet::GetPlayerCount()
|
||||
{
|
||||
if(!_bQNetStubGameRunning)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
DWORD count = 0;
|
||||
for(DWORD localUser = 0; localUser < XUSER_MAX_COUNT; ++localUser)
|
||||
for (DWORD i = 0; i < s_playerCount; i++)
|
||||
{
|
||||
if(_dwQNetStubLocalUsersMask & (1u << localUser))
|
||||
{
|
||||
++count;
|
||||
}
|
||||
if (Win64_IsActivePlayer(&m_player[i], i)) count++;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
QNET_STATE IQNet::GetState() { return _bQNetStubGameRunning ? QNET_STATE_GAME_PLAY : QNET_STATE_IDLE; }
|
||||
bool IQNet::IsHost() { return _bQNetStubIsHost; }
|
||||
HRESULT IQNet::JoinGameFromInviteInfo(DWORD dwUserIndex, DWORD dwUserMask, const INVITE_INFO *pInviteInfo)
|
||||
QNET_STATE IQNet::GetState() { return _iQNetStubState; }
|
||||
bool IQNet::IsHost() { return s_isHosting; }
|
||||
HRESULT IQNet::JoinGameFromInviteInfo(DWORD dwUserIndex, DWORD dwUserMask, const INVITE_INFO *pInviteInfo) { return S_OK; }
|
||||
void IQNet::HostGame() { _iQNetStubState = QNET_STATE_SESSION_STARTING; s_isHosting = true; }
|
||||
void IQNet::ClientJoinGame()
|
||||
{
|
||||
UNREFERENCED_PARAMETER(dwUserIndex);
|
||||
UNREFERENCED_PARAMETER(pInviteInfo);
|
||||
_bQNetStubIsHost = false;
|
||||
_bQNetStubGameRunning = true;
|
||||
_dwQNetStubLocalUsersMask = StubClampLocalUserMask(dwUserMask);
|
||||
if(StubSupportsLocalUser(dwUserIndex))
|
||||
_iQNetStubState = QNET_STATE_SESSION_STARTING;
|
||||
s_isHosting = false;
|
||||
|
||||
for (int i = 0; i < MINECRAFT_NET_MAX_PLAYERS; i++)
|
||||
{
|
||||
_dwQNetStubLocalUsersMask |= (1u << dwUserIndex);
|
||||
m_player[i].m_smallId = (BYTE)i;
|
||||
m_player[i].m_isRemote = true;
|
||||
m_player[i].m_isHostPlayer = false;
|
||||
m_player[i].m_gamertag[0] = 0;
|
||||
m_player[i].SetCustomDataValue(0);
|
||||
}
|
||||
_dwQNetStubLocalUsersMask = StubClampLocalUserMask(_dwQNetStubLocalUsersMask);
|
||||
return S_OK;
|
||||
}
|
||||
void IQNet::HostGame()
|
||||
{
|
||||
_bQNetStubIsHost = true;
|
||||
_bQNetStubGameRunning = true;
|
||||
_dwQNetStubLocalUsersMask = (1u << 0);
|
||||
}
|
||||
void IQNet::EndGame()
|
||||
{
|
||||
_bQNetStubGameRunning = false;
|
||||
_bQNetStubIsHost = true;
|
||||
_dwQNetStubLocalUsersMask = (1u << 0);
|
||||
_iQNetStubState = QNET_STATE_IDLE;
|
||||
s_isHosting = false;
|
||||
s_playerCount = 1;
|
||||
for (int i = 0; i < MINECRAFT_NET_MAX_PLAYERS; i++)
|
||||
{
|
||||
m_player[i].m_smallId = (BYTE)i;
|
||||
m_player[i].m_isRemote = false;
|
||||
m_player[i].m_isHostPlayer = false;
|
||||
m_player[i].m_gamertag[0] = 0;
|
||||
m_player[i].SetCustomDataValue(0);
|
||||
}
|
||||
}
|
||||
|
||||
DWORD MinecraftDynamicConfigurations::GetTrialTime() { return DYNAMIC_CONFIG_DEFAULT_TRIAL_TIME; }
|
||||
@@ -766,7 +758,6 @@ DWORD XEnableGuestSignin(BOOL fEnable) { return 0; }
|
||||
#ifdef _WINDOWS64
|
||||
static void *profileData[4];
|
||||
static bool s_bProfileIsFullVersion;
|
||||
static int s_iPrimaryPad = 0;
|
||||
void C_4JProfile::Initialise( DWORD dwTitleID,
|
||||
DWORD dwOfferID,
|
||||
unsigned short usProfileVersion,
|
||||
@@ -836,21 +827,8 @@ void C_4JProfile::SetTrialTextStringTable(CXuiStringTable *pStringTable,int i
|
||||
void C_4JProfile::SetTrialAwardText(eAwardType AwardType,int iTitle,int iText) {}
|
||||
int C_4JProfile::GetLockedProfile() { return 0; }
|
||||
void C_4JProfile::SetLockedProfile(int iProf) {}
|
||||
bool C_4JProfile::IsSignedIn(int iQuadrant)
|
||||
{
|
||||
if(iQuadrant < 0 || iQuadrant >= (int)XUSER_MAX_COUNT)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if(iQuadrant == 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return InputManager.IsPadConnected(iQuadrant);
|
||||
}
|
||||
bool C_4JProfile::IsSignedInLive(int iProf) { return IsSignedIn(iProf); }
|
||||
bool C_4JProfile::IsSignedIn(int iQuadrant) { return ( iQuadrant == 0); }
|
||||
bool C_4JProfile::IsSignedInLive(int iProf) { return true; }
|
||||
bool C_4JProfile::IsGuest(int iQuadrant) { return false; }
|
||||
UINT C_4JProfile::RequestSignInUI(bool bFromInvite,bool bLocalGame,bool bNoGuestsAllowed,bool bMultiplayerSignIn,bool bAddUser, int( *Func)(LPVOID,const bool, const int iPad),LPVOID lpParam,int iQuadrant) { return 0; }
|
||||
UINT C_4JProfile::DisplayOfflineProfile(int( *Func)(LPVOID,const bool, const int iPad),LPVOID lpParam,int iQuadrant) { return 0; }
|
||||
@@ -859,18 +837,23 @@ void C_4JProfile::SetPrimaryPlayerChanged(bool bVal) {}
|
||||
bool C_4JProfile::QuerySigninStatus(void) { return true; }
|
||||
void C_4JProfile::GetXUID(int iPad, PlayerUID *pXuid,bool bOnlineXuid)
|
||||
{
|
||||
EnsureStubIdentity();
|
||||
if(pXuid == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ULONGLONG value = s_stubXuidBase + (ULONGLONG)(iPad & 0x0F);
|
||||
if(bOnlineXuid)
|
||||
#ifdef _WINDOWS64
|
||||
if (iPad != 0)
|
||||
{
|
||||
value |= 0x100000000ULL;
|
||||
*pXuid = INVALID_XUID;
|
||||
return;
|
||||
}
|
||||
*pXuid = value;
|
||||
if (IQNet::s_isHosting)
|
||||
*pXuid = 0xe000d45248242f2e;
|
||||
else
|
||||
*pXuid = 0xe000d45248242f2e + WinsockNetLayer::GetLocalSmallId();
|
||||
#else
|
||||
*pXuid = 0xe000d45248242f2e + iPad;
|
||||
#endif
|
||||
}
|
||||
BOOL C_4JProfile::AreXUIDSEqual(PlayerUID xuid1,PlayerUID xuid2) { return xuid1 == xuid2; }
|
||||
BOOL C_4JProfile::XUIDIsGuest(PlayerUID xuid) { return false; }
|
||||
@@ -891,45 +874,17 @@ void C_4JProfile::AllowedPlayerCreatedContent(int iPad, bool thisQuadrantOnly
|
||||
BOOL C_4JProfile::CanViewPlayerCreatedContent(int iPad, bool thisQuadrantOnly, PPlayerUID pXuids, DWORD dwXuidCount ) { return true; }
|
||||
bool C_4JProfile::GetProfileAvatar(int iPad,int( *Func)(LPVOID lpParam,PBYTE pbThumbnail,DWORD dwThumbnailBytes), LPVOID lpParam) { return false; }
|
||||
void C_4JProfile::CancelProfileAvatarRequest() {}
|
||||
int C_4JProfile::GetPrimaryPad() { return s_iPrimaryPad; }
|
||||
void C_4JProfile::SetPrimaryPad(int iPad)
|
||||
{
|
||||
if(iPad >= 0 && iPad < (int)XUSER_MAX_COUNT && IsSignedIn(iPad))
|
||||
{
|
||||
s_iPrimaryPad = iPad;
|
||||
}
|
||||
else
|
||||
{
|
||||
s_iPrimaryPad = 0;
|
||||
}
|
||||
}
|
||||
int C_4JProfile::GetPrimaryPad() { return 0; }
|
||||
void C_4JProfile::SetPrimaryPad(int iPad) {}
|
||||
#ifdef _DURANGO
|
||||
char fakeGamerTag[32] = "PlayerName";
|
||||
void SetFakeGamertag(char *name){ strcpy_s(fakeGamerTag, name); }
|
||||
char* C_4JProfile::GetGamertag(int iPad){ return fakeGamerTag; }
|
||||
#else
|
||||
char* C_4JProfile::GetGamertag(int iPad)
|
||||
{
|
||||
EnsureStubIdentity();
|
||||
if(iPad < 0 || iPad >= (int)XUSER_MAX_COUNT)
|
||||
{
|
||||
iPad = 0;
|
||||
}
|
||||
return s_stubGamertagA[iPad];
|
||||
}
|
||||
wstring C_4JProfile::GetDisplayName(int iPad)
|
||||
{
|
||||
EnsureStubIdentity();
|
||||
if(iPad < 0 || iPad >= (int)XUSER_MAX_COUNT)
|
||||
{
|
||||
iPad = 0;
|
||||
}
|
||||
wchar_t displayName[32];
|
||||
swprintf(displayName, 32, L"%S", s_stubGamertagA[iPad]);
|
||||
return displayName;
|
||||
}
|
||||
char* C_4JProfile::GetGamertag(int iPad){ extern char g_Win64Username[17]; return g_Win64Username; }
|
||||
wstring C_4JProfile::GetDisplayName(int iPad){ extern wchar_t g_Win64UsernameW[17]; return g_Win64UsernameW; }
|
||||
#endif
|
||||
bool C_4JProfile::IsFullVersion() { return true; }
|
||||
bool C_4JProfile::IsFullVersion() { return s_bProfileIsFullVersion; }
|
||||
void C_4JProfile::SetSignInChangeCallback(void ( *Func)(LPVOID, bool, unsigned int),LPVOID lpParam) {}
|
||||
void C_4JProfile::SetNotificationsCallback(void ( *Func)(LPVOID, DWORD, unsigned int),LPVOID lpParam) {}
|
||||
bool C_4JProfile::RegionIsNorthAmerica(void) { return false; }
|
||||
@@ -950,10 +905,6 @@ int C_4JProfile::SetOldProfileVersionCallback(int( *Func)(LPVOID,unsigned ch
|
||||
// To store the dashboard preferences for controller flipped, etc.
|
||||
C_4JProfile::PROFILESETTINGS ProfileSettingsA[XUSER_MAX_COUNT];
|
||||
|
||||
#define MAX_AWARDS 32
|
||||
|
||||
static bool s_awardsUnlocked[XUSER_MAX_COUNT][MAX_AWARDS] = {};
|
||||
|
||||
C_4JProfile::PROFILESETTINGS * C_4JProfile::GetDashboardProfileSettings(int iPad) { return &ProfileSettingsA[iPad]; }
|
||||
void C_4JProfile::WriteToProfile(int iQuadrant, bool bGameDefinedDataChanged, bool bOverride5MinuteLimitOnProfileWrites) {}
|
||||
void C_4JProfile::ForceQueuedProfileWrites(int iPad) {}
|
||||
@@ -979,24 +930,9 @@ void C_4JProfile::RegisterAward(int iAwardNumber,int iGamerconfigID, eAwardTy
|
||||
CXuiStringTable*pStringTable, int iTitleStr, int iTextStr, int iAcceptStr, char *pszThemeName, unsigned int ulThemeSize) {}
|
||||
int C_4JProfile::GetAwardId(int iAwardNumber) { return 0; }
|
||||
eAwardType C_4JProfile::GetAwardType(int iAwardNumber) { return eAwardType_Achievement; }
|
||||
bool C_4JProfile::CanBeAwarded(int iQuadrant, int iAwardNumber)
|
||||
{
|
||||
if (iQuadrant < 0 || iQuadrant >= XUSER_MAX_COUNT) return false;
|
||||
if (iAwardNumber < 0 || iAwardNumber >= MAX_AWARDS) return false;
|
||||
return !s_awardsUnlocked[iQuadrant][iAwardNumber];
|
||||
}
|
||||
void C_4JProfile::Award(int iQuadrant, int iAwardNumber, bool bForce)
|
||||
{
|
||||
if (iQuadrant < 0 || iQuadrant >= XUSER_MAX_COUNT) return;
|
||||
if (iAwardNumber < 0 || iAwardNumber >= MAX_AWARDS) return;
|
||||
s_awardsUnlocked[iQuadrant][iAwardNumber] = true;
|
||||
}
|
||||
bool C_4JProfile::IsAwardsFlagSet(int iQuadrant, int iAward)
|
||||
{
|
||||
if (iQuadrant < 0 || iQuadrant >= XUSER_MAX_COUNT) return false;
|
||||
if (iAward < 0 || iAward >= MAX_AWARDS) return false;
|
||||
return s_awardsUnlocked[iQuadrant][iAward];
|
||||
}
|
||||
bool C_4JProfile::CanBeAwarded(int iQuadrant, int iAwardNumber) { return false; }
|
||||
void C_4JProfile::Award(int iQuadrant, int iAwardNumber, bool bForce) {}
|
||||
bool C_4JProfile::IsAwardsFlagSet(int iQuadrant, int iAward) { return false; }
|
||||
void C_4JProfile::RichPresenceInit(int iPresenceCount, int iContextCount) {}
|
||||
void C_4JProfile::RegisterRichPresenceContext(int iGameConfigContextID) {}
|
||||
void C_4JProfile::SetRichPresenceContextValue(int iPad,int iContextID, int iVal) {}
|
||||
@@ -1030,6 +966,17 @@ void C_4JProfile::SetCurrentGameActivity(int iPad,int iNewPresence, bool bSet
|
||||
case 4: // In-game Online
|
||||
strncpy_s(activity.details, sizeof(activity.details), "Playing Minecraft", _TRUNCATE);
|
||||
strncpy_s(activity.state, sizeof(activity.state), "Multiplayer", _TRUNCATE);
|
||||
|
||||
activity.instance = true;
|
||||
|
||||
sprintf_s(activity.party.id, sizeof(activity.party.id), "lce-whisper-session%lu", GetCurrentProcessId());
|
||||
activity.party.size.current_size = (int32_t)g_NetworkManager.GetPlayerCount();
|
||||
activity.party.size.max_size = (int32_t)MINECRAFT_NET_MAX_PLAYERS;
|
||||
|
||||
if (g_NetworkManager.IsHost() && s_DiscordJoinSecret[0] != '\0')
|
||||
{
|
||||
strncpy_s(activity.secrets.join, sizeof(activity.secrets.join), s_DiscordJoinSecret, _TRUNCATE);
|
||||
}
|
||||
break;
|
||||
case 5: // In-game Singleplayer
|
||||
strncpy_s(activity.details, sizeof(activity.details), "Playing Minecraft", _TRUNCATE);
|
||||
@@ -1052,12 +999,6 @@ void C_4JProfile::SetCurrentGameActivity(int iPad,int iNewPresence, bool bSet
|
||||
[](void *data, enum EDiscordResult result) {
|
||||
printf("update_activity result: %d\n", result);
|
||||
});
|
||||
|
||||
for(int i = 0; i < 50; ++i)
|
||||
{
|
||||
discordCore->run_callbacks(discordCore);
|
||||
Sleep(10);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
void C_4JProfile::DisplayFullVersionPurchase(bool bRequired, int iQuadrant, int iUpsellParam) {}
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
void Discord_SetJoinSecret(const char* hostIp, WORD port);
|
||||
bool Discord_HasPendingJoin(char* outIP, int* outPort);
|
||||
void TickDiscord();
|
||||
@@ -928,7 +928,7 @@ void GameRenderer::updateLightTexture(float a)
|
||||
}
|
||||
}
|
||||
|
||||
float brightness = 0.0f; // 4J - TODO - was mc->options->gamma;
|
||||
float brightness = mc->options->gamma; // 4J - TODO - was mc->options->gamma;
|
||||
if (_r > 1) _r = 1;
|
||||
if (_g > 1) _g = 1;
|
||||
if (_b > 1) _b = 1;
|
||||
|
||||
@@ -46,7 +46,7 @@ void Input::tick(LocalPlayer *player)
|
||||
float kbXA = 0.0f;
|
||||
float kbYA = 0.0f;
|
||||
#ifdef _WINDOWS64
|
||||
if (iPad == 0 && g_KBMInput.IsMouseGrabbed())
|
||||
if (iPad == 0 && g_KBMInput.IsMouseGrabbed() && g_KBMInput.IsKBMActive())
|
||||
{
|
||||
if( pMinecraft->localgameModes[iPad]->isInputAllowed(MINECRAFT_ACTION_LEFT) || pMinecraft->localgameModes[iPad]->isInputAllowed(MINECRAFT_ACTION_RIGHT) )
|
||||
kbXA = g_KBMInput.GetMoveX();
|
||||
@@ -94,7 +94,7 @@ void Input::tick(LocalPlayer *player)
|
||||
}
|
||||
|
||||
#ifdef _WINDOWS64
|
||||
if (iPad == 0 && g_KBMInput.IsMouseGrabbed())
|
||||
if (iPad == 0 && g_KBMInput.IsMouseGrabbed() && g_KBMInput.IsKBMActive())
|
||||
{
|
||||
// Left Shift = sneak (hold to crouch)
|
||||
if (pMinecraft->localgameModes[iPad]->isInputAllowed(MINECRAFT_ACTION_SNEAK_TOGGLE))
|
||||
@@ -166,7 +166,7 @@ void Input::tick(LocalPlayer *player)
|
||||
float turnY = ty * abs(ty) * turnSpeed;
|
||||
|
||||
#ifdef _WINDOWS64
|
||||
if (iPad == 0 && g_KBMInput.IsMouseGrabbed())
|
||||
if (iPad == 0 && g_KBMInput.IsMouseGrabbed() && g_KBMInput.IsKBMActive())
|
||||
{
|
||||
float mouseSensitivity = ((float)app.GetGameSettings(iPad,eGameSetting_Sensitivity_InGame)) / 100.0f;
|
||||
float mouseLookScale = 5.0f;
|
||||
@@ -190,7 +190,7 @@ void Input::tick(LocalPlayer *player)
|
||||
unsigned int jump = InputManager.GetValue(iPad, MINECRAFT_ACTION_JUMP);
|
||||
bool kbJump = false;
|
||||
#ifdef _WINDOWS64
|
||||
kbJump = (iPad == 0) && g_KBMInput.IsMouseGrabbed() && g_KBMInput.IsKeyDown(KeyboardMouseInput::KEY_JUMP);
|
||||
kbJump = (iPad == 0) && g_KBMInput.IsMouseGrabbed() && g_KBMInput.IsKBMActive() && g_KBMInput.IsKeyDown(KeyboardMouseInput::KEY_JUMP);
|
||||
#endif
|
||||
if( (jump > 0 || kbJump) && pMinecraft->localgameModes[iPad]->isInputAllowed(MINECRAFT_ACTION_JUMP) )
|
||||
jumping = true;
|
||||
|
||||
@@ -35,6 +35,8 @@ void KeyboardMouseInput::Init()
|
||||
m_mouseGrabbed = false;
|
||||
m_cursorHiddenForUI = false;
|
||||
m_windowFocused = true;
|
||||
m_kbmActive = true;
|
||||
m_screenWantsCursorHidden = false;
|
||||
m_hasInput = false;
|
||||
|
||||
RAWINPUTDEVICE rid;
|
||||
|
||||
@@ -64,6 +64,12 @@ public:
|
||||
void SetWindowFocused(bool focused);
|
||||
bool IsWindowFocused() const { return m_windowFocused; }
|
||||
|
||||
void SetKBMActive(bool active) { m_kbmActive = active; }
|
||||
bool IsKBMActive() const { return m_kbmActive; }
|
||||
|
||||
void SetScreenCursorHidden(bool hidden) { m_screenWantsCursorHidden = hidden; }
|
||||
bool IsScreenCursorHidden() const { return m_screenWantsCursorHidden; }
|
||||
|
||||
bool HasAnyInput() const { return m_hasInput; }
|
||||
|
||||
float GetMoveX() const;
|
||||
@@ -106,6 +112,9 @@ private:
|
||||
|
||||
bool m_windowFocused;
|
||||
|
||||
bool m_kbmActive;
|
||||
bool m_screenWantsCursorHidden;
|
||||
|
||||
bool m_hasInput;
|
||||
};
|
||||
|
||||
|
||||
@@ -1941,7 +1941,12 @@ bool LevelRenderer::updateDirtyChunks()
|
||||
{
|
||||
if( (!onlyRebuild) ||
|
||||
globalChunkFlags[ pClipChunk->globalIdx ] & CHUNK_FLAG_COMPILED ||
|
||||
#ifdef _WINDOWS64
|
||||
( distSq < 96 * 96 ) ) // Always rebuild really near things or else building (say) at tower up into empty blocks when we are low on memory will not create render data
|
||||
#else
|
||||
( distSq < 20 * 20 ) ) // Always rebuild really near things or else building (say) at tower up into empty blocks when we are low on memory will not create render data
|
||||
#endif
|
||||
|
||||
{
|
||||
considered++;
|
||||
// Is this chunk nearer than our nearest?
|
||||
|
||||
@@ -49,6 +49,8 @@ public:
|
||||
static const int MAX_COMMANDBUFFER_ALLOCATIONS = 448 * 1024 * 1024; // 4J - added - hard limit is 512 so giving a lot of headroom here for fragmentation (have seen 16MB lost to fragmentation in multiplayer crash dump before)
|
||||
#elif defined __PS3__
|
||||
static const int MAX_COMMANDBUFFER_ALLOCATIONS = 110 * 1024 * 1024; // 4J - added
|
||||
#elif defined _WINDOWS64
|
||||
static const int MAX_COMMANDBUFFER_ALLOCATIONS = 2047 * 1024 * 1024;
|
||||
#else
|
||||
static const int MAX_COMMANDBUFFER_ALLOCATIONS = 55 * 1024 * 1024; // 4J - added
|
||||
#endif
|
||||
|
||||
@@ -331,6 +331,12 @@ void LocalPlayer::aiStep()
|
||||
}
|
||||
}
|
||||
if (isSneaking()) sprintTriggerTime = 0;
|
||||
|
||||
if (input->sprinting && !isSprinting() && enoughFoodToSprint && !isUsingItem() && !hasEffect(MobEffect::blindness) && input->ya >= runTreshold)
|
||||
{
|
||||
setSprinting(true);
|
||||
}
|
||||
|
||||
// 4J-PB - try not stopping sprint on collision
|
||||
//if (isSprinting() && (input->ya < runTreshold || horizontalCollision || !enoughFoodToSprint))
|
||||
if (isSprinting() && (input->ya < runTreshold || !enoughFoodToSprint))
|
||||
@@ -480,7 +486,10 @@ void LocalPlayer::aiStep()
|
||||
// Check if the player is idle and the rich presence needs updated
|
||||
if( !m_bIsIdle && InputManager.GetIdleSeconds( m_iPad ) > PLAYER_IDLE_TIME )
|
||||
{
|
||||
ProfileManager.SetCurrentGameActivity(m_iPad,CONTEXT_PRESENCE_IDLE,false);
|
||||
#ifdef _WINDOWS64
|
||||
if (!g_NetworkManager.s_pPlatformNetworkManager->IsInSession())
|
||||
#endif
|
||||
ProfileManager.SetCurrentGameActivity(m_iPad, CONTEXT_PRESENCE_IDLE, false);
|
||||
m_bIsIdle = true;
|
||||
}
|
||||
else if ( m_bIsIdle && InputManager.GetIdleSeconds( m_iPad ) < PLAYER_IDLE_TIME )
|
||||
|
||||
@@ -1297,7 +1297,7 @@ if not exist "$(TargetDir)\savedata" mkdir "$(TargetDir)\savedata"</Command>
|
||||
<AdditionalDependencies>d3d11.lib;..\Minecraft.World\x64_Debug\Minecraft.World.lib;%(AdditionalDependencies);XInput9_1_0.lib;..\Minecraft.Client\Windows64\Miles\Lib\mss64.lib;discord_game_sdk.dll.lib</AdditionalDependencies>
|
||||
<ShowProgress>NotSet</ShowProgress>
|
||||
<SuppressStartupBanner>false</SuppressStartupBanner>
|
||||
<AdditionalLibraryDirectories>$(ProjectDir)\discord;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||
<AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||
</Link>
|
||||
<ImageXex>
|
||||
<ConfigurationFile>$(ProjectDir)xbox\xex-dev.xml</ConfigurationFile>
|
||||
@@ -1403,7 +1403,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CU</Comman
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<PrecompiledHeader>NotUsing</PrecompiledHeader>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
<Optimization>Full</Optimization>
|
||||
@@ -1411,9 +1411,9 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CU</Comman
|
||||
<BufferSecurityCheck>false</BufferSecurityCheck>
|
||||
<PrecompiledHeaderOutputFile>$(OutDir)$(ProjectName).pch</PrecompiledHeaderOutputFile>
|
||||
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
||||
<PreprocessorDefinitions>_LARGE_WORLDS;_DEBUG_MENUS_ENABLED;_CRT_NON_CONFORMING_SWPRINTFS;_CRT_SECURE_NO_WARNINGS;_WINDOWS64;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<PreprocessorDefinitions>_LARGE_WORLDS;_DEBUG_MENUS_ENABLED;_CRT_NON_CONFORMING_SWPRINTFS;_CRT_SECURE_NO_WARNINGS;_WINDOWS64;MINIUPNP_STATICLIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<CallAttributedProfiling>Disabled</CallAttributedProfiling>
|
||||
<AdditionalIncludeDirectories>Windows64\Iggy\include;$(ProjectDir);$(ProjectDir)\discord;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<AdditionalIncludeDirectories>Windows64\Iggy\include;$(ProjectDir);$(ProjectDir)\discord;$(ProjectDir)miniupnpc\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<RuntimeTypeInfo>true</RuntimeTypeInfo>
|
||||
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||
<BasicRuntimeChecks>Default</BasicRuntimeChecks>
|
||||
@@ -1423,10 +1423,10 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CU</Comman
|
||||
<Link>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<ProgramDatabaseFile>$(OutDir)$(ProjectName).pdb</ProgramDatabaseFile>
|
||||
<AdditionalDependencies>d3d11.lib;..\Minecraft.World\x64_Release\Minecraft.World.lib;XInput9_1_0.lib;Windows64\Iggy\lib\iggy_w64.lib;discord_game_sdk.dll.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalDependencies>d3d11.lib;..\Minecraft.World\x64_Release\Minecraft.World.lib;XInput9_1_0.lib;Windows64\Iggy\lib\iggy_w64.lib;discord_game_sdk.dll.lib;IPHlpApi.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<ShowProgress>NotSet</ShowProgress>
|
||||
<SuppressStartupBanner>false</SuppressStartupBanner>
|
||||
<AdditionalLibraryDirectories>$(ProjectDir)\discord;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||
<AdditionalLibraryDirectories>$(ProjectDir)\discord;C:\Program Files (x86)\Windows Kits\8.0\Lib\win8\um\x64;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||
</Link>
|
||||
<ImageXex>
|
||||
<ConfigurationFile>$(ProjectDir)xbox\xex-dev.xml</ConfigurationFile>
|
||||
@@ -2938,6 +2938,8 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CU</Comman
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|ORBIS'">true</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='ContentPackage_Vita|ORBIS'">true</ExcludedFromBuild>
|
||||
</None>
|
||||
<None Include="miniupnpc\genminiupnpcstrings.vbs" />
|
||||
<None Include="miniupnpc\miniupnpcstrings.h.in" />
|
||||
<None Include="Network Implementation Notes.txt" />
|
||||
<None Include="Orbis\4JLibs\Libs\4J_Input.a">
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='ContentPackage|Durango'">true</ExcludedFromBuild>
|
||||
@@ -4363,8 +4365,10 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CU</Comman
|
||||
<ClInclude Include="Common\Network\STUNClient.h" />
|
||||
<ClInclude Include="ConnectScreen.h" />
|
||||
<ClInclude Include="KeyboardMouseInput.h" />
|
||||
<ClInclude Include="miniupnpc\miniupnpcstrings.h" />
|
||||
<ClInclude Include="Windows64\Network\NetworkPlayerInterface.h" />
|
||||
<ClInclude Include="Windows64\Network\P2PConnectionManagerWin.h" />
|
||||
<ClInclude Include="Windows64\Network\WinsockNetLayer.h" />
|
||||
<ClInclude Include="Common\Network\NetworkPlayerInterface.h" />
|
||||
<ClInclude Include="Common\Network\PlatformNetworkManagerInterface.h" />
|
||||
<ClInclude Include="Common\Network\PlatformNetworkManagerStub.h">
|
||||
@@ -16609,37 +16613,6 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CU</Comman
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|ORBIS'">true</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='ContentPackage_Vita|ORBIS'">true</ExcludedFromBuild>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Windows64\Windows64_App.h">
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='ContentPackage|Durango'">true</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='CONTENTPACKAGE_SYMBOLS|Durango'">true</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='ReleaseForArt|Durango'">true</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='ContentPackage_NO_TU|Durango'">true</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='DebugContentPackage|Durango'">true</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Durango'">true</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='ContentPackage_Vita|Durango'">true</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Durango'">true</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='ContentPackage|Xbox 360'">true</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='CONTENTPACKAGE_SYMBOLS|Xbox 360'">true</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='ReleaseForArt|Xbox 360'">true</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='ContentPackage_NO_TU|Xbox 360'">true</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Xbox 360'">true</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='ContentPackage_Vita|Xbox 360'">true</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Xbox 360'">true</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='ContentPackage|x64'">false</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='CONTENTPACKAGE_SYMBOLS|x64'">false</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='ReleaseForArt|x64'">false</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='ContentPackage_NO_TU|x64'">false</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">false</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='ContentPackage_Vita|x64'">false</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">false</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='ContentPackage|ORBIS'">true</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='CONTENTPACKAGE_SYMBOLS|ORBIS'">true</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='ReleaseForArt|ORBIS'">true</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='ContentPackage_NO_TU|ORBIS'">true</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|ORBIS'">true</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|ORBIS'">true</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='ContentPackage_Vita|ORBIS'">true</ExcludedFromBuild>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Windows64\Windows64_UIController.h">
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Durango'">true</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='ContentPackage_Vita|Durango'">true</ExcludedFromBuild>
|
||||
@@ -16710,9 +16683,11 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CU</Comman
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='ContentPackage_Vita|ORBIS'">true</ExcludedFromBuild>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Windows64_DLCOffers.h" />
|
||||
<ClInclude Include="Windows64_Minecraft.h" />
|
||||
<ClInclude Include="WolfModel.h" />
|
||||
<ClInclude Include="WolfRenderer.h" />
|
||||
<ClInclude Include="WstringLookup.h" />
|
||||
<ClInclude Include="Extrax64Stubs.h" />
|
||||
<ClInclude Include="Xbox\4JLibs\inc\4J_Input.h">
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='ContentPackage|x64'">true</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='CONTENTPACKAGE_SYMBOLS|x64'">true</ExcludedFromBuild>
|
||||
@@ -21561,11 +21536,24 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CU</Comman
|
||||
<ClCompile Include="Common\Network\GameNetworkManager.cpp" />
|
||||
<ClCompile Include="Common\Network\NATTraversal.cpp" />
|
||||
<ClCompile Include="Common\Network\LANSessionManager.cpp" />
|
||||
<ClCompile Include="Common\Network\PacketRouter.cpp" />
|
||||
<ClCompile Include="Common\Network\STUNClient.cpp" />
|
||||
<ClCompile Include="ConnectScreen.cpp" />
|
||||
<ClCompile Include="KeyboardMouseInput.cpp" />
|
||||
<ClCompile Include="miniupnpc\addr_is_reserved.c" />
|
||||
<ClCompile Include="miniupnpc\connecthostport.c" />
|
||||
<ClCompile Include="miniupnpc\igd_desc_parse.c" />
|
||||
<ClCompile Include="miniupnpc\minisoap.c" />
|
||||
<ClCompile Include="miniupnpc\minissdpc.c" />
|
||||
<ClCompile Include="miniupnpc\miniupnpc.c" />
|
||||
<ClCompile Include="miniupnpc\miniwget.c" />
|
||||
<ClCompile Include="miniupnpc\minixml.c" />
|
||||
<ClCompile Include="miniupnpc\portlistingparse.c" />
|
||||
<ClCompile Include="miniupnpc\receivedata.c" />
|
||||
<ClCompile Include="miniupnpc\upnpcommands.c" />
|
||||
<ClCompile Include="miniupnpc\upnpdev.c" />
|
||||
<ClCompile Include="miniupnpc\upnpreplyparse.c" />
|
||||
<ClCompile Include="Windows64\Network\P2PConnectionManagerWin.cpp" />
|
||||
<ClCompile Include="Windows64\Network\WinsockNetLayer.cpp" />
|
||||
<ClCompile Include="Common\Network\PlatformNetworkManagerStub.cpp">
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='ContentPackage_NO_TU|Xbox 360'">true</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='ContentPackage|Xbox 360'">true</ExcludedFromBuild>
|
||||
|
||||
@@ -881,6 +881,8 @@
|
||||
<None Include="PSVita\Iggy\gdraw\gdraw_shared.inl">
|
||||
<Filter>PSVita\Iggy\gdraw</Filter>
|
||||
</None>
|
||||
<None Include="miniupnpc\miniupnpcstrings.h.in" />
|
||||
<None Include="miniupnpc\genminiupnpcstrings.vbs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="stdafx.h">
|
||||
@@ -2338,9 +2340,6 @@
|
||||
<ClInclude Include="Windows64\Resource.h">
|
||||
<Filter>Windows64</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Windows64\Windows64_App.h">
|
||||
<Filter>Windows64</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Durango\ApplicationView.h">
|
||||
<Filter>Durango\Source Files</Filter>
|
||||
</ClInclude>
|
||||
@@ -2971,6 +2970,9 @@
|
||||
<ClInclude Include="Windows64\Network\P2PConnectionManagerWin.h">
|
||||
<Filter>Common\Source Files\Network</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Windows64\Network\WinsockNetLayer.h">
|
||||
<Filter>Common\Source Files\Network</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="PS3\PS3Extras\EdgeZLib.h">
|
||||
<Filter>PS3\PS3Extras</Filter>
|
||||
</ClInclude>
|
||||
@@ -3658,6 +3660,15 @@
|
||||
<ClInclude Include="KeyboardMouseInput.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Windows64_Minecraft.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="miniupnpc\miniupnpcstrings.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Extrax64Stubs.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="stdafx.cpp">
|
||||
@@ -5085,15 +5096,15 @@
|
||||
<ClCompile Include="Common\Network\LANSessionManager.cpp">
|
||||
<Filter>Common\Source Files\Network</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Common\Network\PacketRouter.cpp">
|
||||
<Filter>Common\Source Files\Network</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Common\Network\STUNClient.cpp">
|
||||
<Filter>Common\Source Files\Network</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Windows64\Network\P2PConnectionManagerWin.cpp">
|
||||
<Filter>Common\Source Files\Network</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Windows64\Network\WinsockNetLayer.cpp">
|
||||
<Filter>Common\Source Files\Network</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="PS3\PS3Extras\EdgeZLib.cpp">
|
||||
<Filter>PS3\PS3Extras</Filter>
|
||||
</ClCompile>
|
||||
@@ -5688,6 +5699,45 @@
|
||||
<ClCompile Include="KeyboardMouseInput.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="miniupnpc\connecthostport.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="miniupnpc\igd_desc_parse.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="miniupnpc\minisoap.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="miniupnpc\minissdpc.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="miniupnpc\miniupnpc.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="miniupnpc\minixml.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="miniupnpc\portlistingparse.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="miniupnpc\receivedata.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="miniupnpc\upnpcommands.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="miniupnpc\upnpdev.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="miniupnpc\miniwget.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="miniupnpc\upnpreplyparse.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="miniupnpc\addr_is_reserved.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Library Include="Xbox\4JLibs\libs\4J_Render_d.lib">
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
#include "Camera.h"
|
||||
#ifdef _WINDOWS64
|
||||
#include "KeyboardMouseInput.h"
|
||||
#include "KeyMapping.h"
|
||||
#endif
|
||||
|
||||
#include "..\Minecraft.World\MobEffect.h"
|
||||
@@ -1470,7 +1471,7 @@ void Minecraft::run_middle()
|
||||
{
|
||||
if(InputManager.ButtonDown(i, MINECRAFT_ACTION_SNEAK_TOGGLE)) localplayers[i]->ullButtonsPressed|=1LL<<MINECRAFT_ACTION_SNEAK_TOGGLE;
|
||||
#ifdef _WINDOWS64
|
||||
if(i == 0 && g_KBMInput.IsKeyDown(KeyboardMouseInput::KEY_SNEAK)) localplayers[i]->ullButtonsPressed|=1LL<<MINECRAFT_ACTION_SNEAK_TOGGLE;
|
||||
if(i == 0 && g_KBMInput.IsKBMActive() && g_KBMInput.IsKeyDown(KeyboardMouseInput::KEY_SNEAK)) localplayers[i]->ullButtonsPressed|=1LL<<MINECRAFT_ACTION_SNEAK_TOGGLE;
|
||||
#endif
|
||||
}
|
||||
else
|
||||
@@ -1481,7 +1482,7 @@ void Minecraft::run_middle()
|
||||
if(InputManager.ButtonPressed(i, MINECRAFT_ACTION_GAME_INFO)) localplayers[i]->ullButtonsPressed|=1LL<<MINECRAFT_ACTION_GAME_INFO;
|
||||
|
||||
#ifdef _WINDOWS64
|
||||
if (i == 0)
|
||||
if (i == 0 && g_KBMInput.IsKBMActive())
|
||||
{
|
||||
if(g_KBMInput.IsMouseButtonPressed(KeyboardMouseInput::MOUSE_LEFT))
|
||||
localplayers[i]->ullButtonsPressed|=1LL<<MINECRAFT_ACTION_ACTION;
|
||||
@@ -1518,12 +1519,6 @@ void Minecraft::run_middle()
|
||||
showFpsCounter = !showFpsCounter;
|
||||
}
|
||||
|
||||
int wheel = g_KBMInput.GetMouseWheel();
|
||||
if (wheel > 0)
|
||||
localplayers[i]->ullButtonsPressed|=1LL<<MINECRAFT_ACTION_RIGHT_SCROLL;
|
||||
else if (wheel < 0)
|
||||
localplayers[i]->ullButtonsPressed|=1LL<<MINECRAFT_ACTION_LEFT_SCROLL;
|
||||
|
||||
for (int slot = 0; slot < 9; slot++)
|
||||
{
|
||||
if (g_KBMInput.IsKeyPressed('1' + slot))
|
||||
@@ -2339,7 +2334,7 @@ void Minecraft::tick(bool bFirst, bool bUpdateTextures)
|
||||
if (screen == NULL && !ui.GetMenuDisplayed(iPad) )
|
||||
{
|
||||
#ifdef _WINDOWS64
|
||||
if (!g_KBMInput.IsMouseGrabbed() && g_KBMInput.IsWindowFocused())
|
||||
if (!g_KBMInput.IsMouseGrabbed() && g_KBMInput.IsWindowFocused() && g_KBMInput.IsKBMActive())
|
||||
{
|
||||
g_KBMInput.SetMouseGrabbed(true);
|
||||
}
|
||||
@@ -3285,7 +3280,7 @@ void Minecraft::tick(bool bFirst, bool bUpdateTextures)
|
||||
wheel = -1;
|
||||
}
|
||||
#ifdef _WINDOWS64
|
||||
if (iPad == 0 && wheel == 0)
|
||||
if (iPad == 0 && wheel == 0 && g_KBMInput.IsKBMActive())
|
||||
{
|
||||
int mw = g_KBMInput.GetMouseWheel();
|
||||
if (mw > 0) wheel = -1;
|
||||
@@ -3325,7 +3320,7 @@ void Minecraft::tick(bool bFirst, bool bUpdateTextures)
|
||||
}
|
||||
|
||||
#ifdef _WINDOWS64
|
||||
bool actionHeld = InputManager.ButtonDown(iPad, MINECRAFT_ACTION_ACTION) || (iPad == 0 && g_KBMInput.IsMouseButtonDown(KeyboardMouseInput::MOUSE_LEFT));
|
||||
bool actionHeld = InputManager.ButtonDown(iPad, MINECRAFT_ACTION_ACTION) || (iPad == 0 && g_KBMInput.IsKBMActive() && g_KBMInput.IsMouseButtonDown(KeyboardMouseInput::MOUSE_LEFT));
|
||||
#else
|
||||
bool actionHeld = InputManager.ButtonDown(iPad, MINECRAFT_ACTION_ACTION);
|
||||
#endif
|
||||
@@ -3356,7 +3351,7 @@ void Minecraft::tick(bool bFirst, bool bUpdateTextures)
|
||||
}
|
||||
*/
|
||||
#ifdef _WINDOWS64
|
||||
bool useHeld = InputManager.ButtonDown(iPad, MINECRAFT_ACTION_USE) || (iPad == 0 && g_KBMInput.IsMouseButtonDown(KeyboardMouseInput::MOUSE_RIGHT));
|
||||
bool useHeld = InputManager.ButtonDown(iPad, MINECRAFT_ACTION_USE) || (iPad == 0 && g_KBMInput.IsKBMActive() && g_KBMInput.IsMouseButtonDown(KeyboardMouseInput::MOUSE_RIGHT));
|
||||
#else
|
||||
bool useHeld = InputManager.ButtonDown(iPad, MINECRAFT_ACTION_USE);
|
||||
#endif
|
||||
@@ -3594,6 +3589,12 @@ void Minecraft::tick(bool bFirst, bool bUpdateTextures)
|
||||
// }
|
||||
// #endif
|
||||
|
||||
|
||||
if(Keyboard::isKeyDown(options->keyChat->key))
|
||||
{
|
||||
setScreen(new ChatScreen());
|
||||
}
|
||||
|
||||
#if 0
|
||||
// 4J - TODO - some replacement for input handling...
|
||||
if (screen == NULL || screen.passEvents)
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -161,12 +161,17 @@ bool CGameNetworkManager::_RunNetworkGame(LPVOID lpParameter)
|
||||
|
||||
success = s_pPlatformNetworkManager->_RunNetworkGame();
|
||||
if(!success)
|
||||
{
|
||||
{
|
||||
app.SetAction(ProfileManager.GetPrimaryPad(),eAppAction_ExitWorld,(void *)TRUE);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
// Client needs QNET_STATE_GAME_PLAY so that IsInGameplay() returns true
|
||||
s_pPlatformNetworkManager->SetGamePlayState();
|
||||
}
|
||||
|
||||
if( g_NetworkManager.IsLeavingGame() ) return false;
|
||||
|
||||
app.SetGameStarted(true);
|
||||
@@ -1391,7 +1396,10 @@ void CGameNetworkManager::CreateSocket( INetworkPlayer *pNetworkPlayer, bool loc
|
||||
Minecraft *pMinecraft = Minecraft::GetInstance();
|
||||
|
||||
Socket *socket = NULL;
|
||||
shared_ptr<MultiplayerLocalPlayer> mpPlayer = pMinecraft->localplayers[pNetworkPlayer->GetUserIndex()];
|
||||
shared_ptr<MultiplayerLocalPlayer> mpPlayer = nullptr;
|
||||
int userIdx = pNetworkPlayer->GetUserIndex();
|
||||
if (userIdx >= 0 && userIdx < XUSER_MAX_COUNT)
|
||||
mpPlayer = pMinecraft->localplayers[userIdx];
|
||||
if( localPlayer && mpPlayer != NULL && mpPlayer->connection != NULL)
|
||||
{
|
||||
// If we already have a MultiplayerLocalPlayer here then we are doing a session type change
|
||||
|
||||
@@ -71,6 +71,7 @@ public:
|
||||
virtual void HostGame(int localUsersMask, bool bOnlineGame, bool bIsPrivate, unsigned char publicSlots = MINECRAFT_NET_MAX_PLAYERS, unsigned char privateSlots = 0) = 0;
|
||||
virtual int JoinGame(FriendSessionInfo *searchResult, int dwLocalUsersMask, int dwPrimaryUserIndex ) = 0;
|
||||
virtual void CancelJoinGame() {};
|
||||
virtual void SetGamePlayState() {};
|
||||
virtual bool SetLocalGame(bool isLocal) = 0;
|
||||
virtual bool IsLocalGame() = 0;
|
||||
virtual void SetPrivateGame(bool isPrivate) = 0;
|
||||
|
||||
@@ -313,6 +313,12 @@ void CPlatformNetworkManagerStub::HandleSignInChange()
|
||||
return;
|
||||
}
|
||||
|
||||
void CPlatformNetworkManagerStub::SetGamePlayState()
|
||||
{
|
||||
extern QNET_STATE _iQNetStubState;
|
||||
_iQNetStubState = QNET_STATE_GAME_PLAY;
|
||||
}
|
||||
|
||||
bool CPlatformNetworkManagerStub::_RunNetworkGame()
|
||||
{
|
||||
return true;
|
||||
|
||||
@@ -55,6 +55,7 @@ public:
|
||||
virtual void HandleSignInChange();
|
||||
|
||||
virtual bool _RunNetworkGame();
|
||||
virtual void SetGamePlayState();
|
||||
|
||||
private:
|
||||
bool isSystemPrimaryPlayer(IQNetPlayer *pQNetPlayer);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,153 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef _WINDOWS64
|
||||
|
||||
#include <WinSock2.h>
|
||||
#include <WS2tcpip.h>
|
||||
#include <vector>
|
||||
#include "..\..\Common\Network\NetworkPlayerInterface.h"
|
||||
|
||||
#pragma comment(lib, "Ws2_32.lib")
|
||||
|
||||
#define WIN64_NET_DEFAULT_PORT 25565
|
||||
#define WIN64_NET_MAX_CLIENTS 7
|
||||
#define WIN64_NET_RECV_BUFFER_SIZE 65536
|
||||
#define WIN64_NET_MAX_PACKET_SIZE (4 * 1024 * 1024)
|
||||
#define WIN64_LAN_DISCOVERY_PORT 25566
|
||||
#define WIN64_LAN_BROADCAST_MAGIC 0x4D434C4E
|
||||
|
||||
class Socket;
|
||||
|
||||
#pragma pack(push, 1)
|
||||
struct Win64LANBroadcast
|
||||
{
|
||||
DWORD magic;
|
||||
WORD netVersion;
|
||||
WORD gamePort;
|
||||
wchar_t hostName[32];
|
||||
BYTE playerCount;
|
||||
BYTE maxPlayers;
|
||||
DWORD gameHostSettings;
|
||||
DWORD texturePackParentId;
|
||||
BYTE subTexturePackId;
|
||||
BYTE isJoinable;
|
||||
};
|
||||
#pragma pack(pop)
|
||||
|
||||
struct Win64LANSession
|
||||
{
|
||||
char hostIP[64];
|
||||
int hostPort;
|
||||
wchar_t hostName[32];
|
||||
unsigned short netVersion;
|
||||
unsigned char playerCount;
|
||||
unsigned char maxPlayers;
|
||||
unsigned int gameHostSettings;
|
||||
unsigned int texturePackParentId;
|
||||
unsigned char subTexturePackId;
|
||||
bool isJoinable;
|
||||
DWORD lastSeenTick;
|
||||
};
|
||||
|
||||
struct Win64RemoteConnection
|
||||
{
|
||||
SOCKET tcpSocket;
|
||||
BYTE smallId;
|
||||
HANDLE recvThread;
|
||||
volatile bool active;
|
||||
};
|
||||
|
||||
class WinsockNetLayer
|
||||
{
|
||||
public:
|
||||
static bool s_upnpMapped;
|
||||
static char s_externalIP[64];
|
||||
static void CleanupUPnP(int port);
|
||||
|
||||
static bool Initialize();
|
||||
static void Shutdown();
|
||||
|
||||
static bool HostGame(int port);
|
||||
static bool JoinGame(const char *ip, int port);
|
||||
|
||||
static bool SendToSmallId(BYTE targetSmallId, const void *data, int dataSize);
|
||||
static bool SendOnSocket(SOCKET sock, const void *data, int dataSize);
|
||||
|
||||
static bool IsHosting() { return s_isHost; }
|
||||
static bool IsConnected() { return s_connected; }
|
||||
static bool IsActive() { return s_active; }
|
||||
|
||||
static BYTE GetLocalSmallId() { return s_localSmallId; }
|
||||
static BYTE GetHostSmallId() { return s_hostSmallId; }
|
||||
|
||||
static SOCKET GetSocketForSmallId(BYTE smallId);
|
||||
|
||||
static void HandleDataReceived(BYTE fromSmallId, BYTE toSmallId, unsigned char *data, unsigned int dataSize);
|
||||
|
||||
static bool PopDisconnectedSmallId(BYTE *outSmallId);
|
||||
static void PushFreeSmallId(BYTE smallId);
|
||||
static void CloseConnectionBySmallId(BYTE smallId);
|
||||
|
||||
static bool StartAdvertising(int gamePort, const wchar_t *hostName, unsigned int gameSettings, unsigned int texPackId, unsigned char subTexId, unsigned short netVer);
|
||||
static void StopAdvertising();
|
||||
static void UpdateAdvertisePlayerCount(BYTE count);
|
||||
static void UpdateAdvertiseJoinable(bool joinable);
|
||||
|
||||
static bool StartDiscovery();
|
||||
static void StopDiscovery();
|
||||
static std::vector<Win64LANSession> GetDiscoveredSessions();
|
||||
|
||||
static int GetHostPort() { return s_hostGamePort; }
|
||||
|
||||
private:
|
||||
static DWORD WINAPI AcceptThreadProc(LPVOID param);
|
||||
static DWORD WINAPI RecvThreadProc(LPVOID param);
|
||||
static DWORD WINAPI ClientRecvThreadProc(LPVOID param);
|
||||
static DWORD WINAPI AdvertiseThreadProc(LPVOID param);
|
||||
static DWORD WINAPI DiscoveryThreadProc(LPVOID param);
|
||||
|
||||
static SOCKET s_listenSocket;
|
||||
static SOCKET s_hostConnectionSocket;
|
||||
static HANDLE s_acceptThread;
|
||||
static HANDLE s_clientRecvThread;
|
||||
|
||||
static bool s_isHost;
|
||||
static bool s_connected;
|
||||
static bool s_active;
|
||||
static bool s_initialized;
|
||||
|
||||
static BYTE s_localSmallId;
|
||||
static BYTE s_hostSmallId;
|
||||
static BYTE s_nextSmallId;
|
||||
|
||||
static CRITICAL_SECTION s_sendLock;
|
||||
static CRITICAL_SECTION s_connectionsLock;
|
||||
|
||||
static std::vector<Win64RemoteConnection> s_connections;
|
||||
|
||||
static SOCKET s_advertiseSock;
|
||||
static HANDLE s_advertiseThread;
|
||||
static volatile bool s_advertising;
|
||||
static Win64LANBroadcast s_advertiseData;
|
||||
static CRITICAL_SECTION s_advertiseLock;
|
||||
static int s_hostGamePort;
|
||||
|
||||
static SOCKET s_discoverySock;
|
||||
static HANDLE s_discoveryThread;
|
||||
static volatile bool s_discovering;
|
||||
static CRITICAL_SECTION s_discoveryLock;
|
||||
static std::vector<Win64LANSession> s_discoveredSessions;
|
||||
|
||||
static CRITICAL_SECTION s_disconnectLock;
|
||||
static std::vector<BYTE> s_disconnectedSmallIds;
|
||||
|
||||
static CRITICAL_SECTION s_freeSmallIdLock;
|
||||
static std::vector<BYTE> s_freeSmallIds;
|
||||
};
|
||||
|
||||
extern bool g_Win64MultiplayerHost;
|
||||
extern bool g_Win64MultiplayerJoin;
|
||||
extern int g_Win64MultiplayerPort;
|
||||
extern char g_Win64MultiplayerIP[256];
|
||||
|
||||
#endif
|
||||
@@ -26,6 +26,8 @@ void CConsoleMinecraftApp::StoreLaunchData()
|
||||
}
|
||||
void CConsoleMinecraftApp::ExitGame()
|
||||
{
|
||||
extern HWND g_hWnd;
|
||||
PostMessage(g_hWnd, WM_CLOSE, 0, 0);
|
||||
}
|
||||
void CConsoleMinecraftApp::FatalLoadError()
|
||||
{
|
||||
@@ -55,7 +57,8 @@ void CConsoleMinecraftApp::TemporaryCreateGameStart()
|
||||
Minecraft *pMinecraft=Minecraft::GetInstance();
|
||||
app.ReleaseSaveThumbnail();
|
||||
ProfileManager.SetLockedProfile(0);
|
||||
pMinecraft->user->name = L"Windows";
|
||||
extern wchar_t g_Win64UsernameW[17];
|
||||
pMinecraft->user->name = g_Win64UsernameW;
|
||||
app.ApplyGameSettingsChanged(0);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////// From CScene_MultiGameJoinLoad::OnInit
|
||||
|
||||
@@ -37,9 +37,13 @@
|
||||
#include "Resource.h"
|
||||
#include "..\..\Minecraft.World\compression.h"
|
||||
#include "..\..\Minecraft.World\OldChunkStorage.h"
|
||||
#include "Network\WinsockNetLayer.h"
|
||||
#include "Extrax64Stubs.h"
|
||||
|
||||
#include "Xbox/resource.h"
|
||||
|
||||
#include "Windows64_Minecraft.h"
|
||||
|
||||
HINSTANCE hMyInst;
|
||||
LRESULT CALLBACK DlgProc(HWND hWndDlg, UINT Msg, WPARAM wParam, LPARAM lParam);
|
||||
char chGlobalText[256];
|
||||
@@ -80,6 +84,9 @@ BOOL g_bWidescreen = TRUE;
|
||||
int g_iScreenWidth = 1920;
|
||||
int g_iScreenHeight = 1080;
|
||||
|
||||
char g_Win64Username[17] = {0};
|
||||
wchar_t g_Win64UsernameW[17] = {0};
|
||||
|
||||
void DefineActions(void)
|
||||
{
|
||||
// The app needs to define the actions required, and the possible mappings for these
|
||||
@@ -289,6 +296,11 @@ void MemSect(int sect)
|
||||
HINSTANCE g_hInst = NULL;
|
||||
HWND g_hWnd = NULL;
|
||||
|
||||
HWND GetMinecraftWindowHWND()
|
||||
{
|
||||
return g_hWnd;
|
||||
}
|
||||
|
||||
static bool g_isFullscreen = false;
|
||||
static RECT g_windowedRect = {};
|
||||
static LONG g_windowedStyle = 0;
|
||||
@@ -335,6 +347,52 @@ D3D_FEATURE_LEVEL g_featureLevel = D3D_FEATURE_LEVEL_11_0;
|
||||
ID3D11Device* g_pd3dDevice = NULL;
|
||||
ID3D11DeviceContext* g_pImmediateContext = NULL;
|
||||
IDXGISwapChain* g_pSwapChain = NULL;
|
||||
|
||||
static WORD g_originalGammaRamp[3][256];
|
||||
static bool g_gammaRampSaved = false;
|
||||
|
||||
void Windows64_UpdateGamma(unsigned short usGamma)
|
||||
{
|
||||
if (!g_hWnd) return;
|
||||
|
||||
HDC hdc = GetDC(g_hWnd);
|
||||
if (!hdc) return;
|
||||
|
||||
if (!g_gammaRampSaved)
|
||||
{
|
||||
GetDeviceGammaRamp(hdc, g_originalGammaRamp);
|
||||
g_gammaRampSaved = true;
|
||||
}
|
||||
|
||||
float gamma = (float)usGamma / 32768.0f;
|
||||
if (gamma < 0.01f) gamma = 0.01f;
|
||||
if (gamma > 1.0f) gamma = 1.0f;
|
||||
|
||||
float invGamma = 1.0f / (0.5f + gamma * 0.5f);
|
||||
|
||||
WORD ramp[3][256];
|
||||
for (int i = 0; i < 256; i++)
|
||||
{
|
||||
float normalized = (float)i / 255.0f;
|
||||
float corrected = powf(normalized, invGamma);
|
||||
WORD val = (WORD)(corrected * 65535.0f + 0.5f);
|
||||
ramp[0][i] = val;
|
||||
ramp[1][i] = val;
|
||||
ramp[2][i] = val;
|
||||
}
|
||||
|
||||
SetDeviceGammaRamp(hdc, ramp);
|
||||
ReleaseDC(g_hWnd, hdc);
|
||||
}
|
||||
|
||||
void Windows64_RestoreGamma()
|
||||
{
|
||||
if (!g_gammaRampSaved || !g_hWnd) return;
|
||||
HDC hdc = GetDC(g_hWnd);
|
||||
if (!hdc) return;
|
||||
SetDeviceGammaRamp(hdc, g_originalGammaRamp);
|
||||
ReleaseDC(g_hWnd, hdc);
|
||||
}
|
||||
ID3D11RenderTargetView* g_pRenderTargetView = NULL;
|
||||
ID3D11DepthStencilView* g_pDepthStencilView = NULL;
|
||||
ID3D11Texture2D* g_pDepthStencilBuffer = NULL;
|
||||
@@ -822,6 +880,9 @@ void Render()
|
||||
//--------------------------------------------------------------------------------------
|
||||
void CleanupDevice()
|
||||
{
|
||||
extern void Windows64_RestoreGamma();
|
||||
Windows64_RestoreGamma();
|
||||
|
||||
if( g_pImmediateContext ) g_pImmediateContext->ClearState();
|
||||
|
||||
if( g_pRenderTargetView ) g_pRenderTargetView->Release();
|
||||
@@ -870,8 +931,32 @@ int APIENTRY _tWinMain(_In_ HINSTANCE hInstance,
|
||||
//g_iScreenWidth = 960;
|
||||
//g_iScreenHeight = 544;
|
||||
}
|
||||
|
||||
char cmdLineA[1024];
|
||||
strncpy_s(cmdLineA, sizeof(cmdLineA), lpCmdLine, _TRUNCATE);
|
||||
|
||||
char *nameArg = strstr(cmdLineA, "-name ");
|
||||
if (nameArg)
|
||||
{
|
||||
nameArg += 6;
|
||||
while (*nameArg == ' ') nameArg++;
|
||||
char nameBuf[17];
|
||||
int n = 0;
|
||||
while (nameArg[n] && nameArg[n] != ' ' && n < 16) { nameBuf[n] = nameArg[n]; n++; }
|
||||
nameBuf[n] = 0;
|
||||
strncpy_s(g_Win64Username, 17, nameBuf, _TRUNCATE);
|
||||
}
|
||||
}
|
||||
|
||||
if (g_Win64Username[0] == 0)
|
||||
{
|
||||
DWORD sz = 17;
|
||||
if (!GetUserNameA(g_Win64Username, &sz))
|
||||
strncpy_s(g_Win64Username, 17, "Player", _TRUNCATE);
|
||||
g_Win64Username[16] = 0;
|
||||
}
|
||||
|
||||
MultiByteToWideChar(CP_ACP, 0, g_Win64Username, -1, g_Win64UsernameW, 17);
|
||||
|
||||
// Initialize global strings
|
||||
MyRegisterClass(hInstance);
|
||||
@@ -1021,6 +1106,22 @@ int APIENTRY _tWinMain(_In_ HINSTANCE hInstance,
|
||||
ProfileManager.SetNotificationsCallback(&CConsoleMinecraftApp::NotificationsCallback,(LPVOID)&app);
|
||||
|
||||
#endif
|
||||
|
||||
// Ensure the GameHDD save directory exists at runtime (the 4J_Storage lib expects it)
|
||||
{
|
||||
wchar_t exePath[MAX_PATH];
|
||||
if (GetModuleFileNameW(NULL, exePath, MAX_PATH))
|
||||
{
|
||||
wstring exeDir(exePath);
|
||||
size_t lastSlash = exeDir.find_last_of(L"\\/");
|
||||
if (lastSlash != wstring::npos)
|
||||
exeDir = exeDir.substr(0, lastSlash);
|
||||
wstring gameHDDPath = exeDir + L"\\Windows64\\GameHDD";
|
||||
CreateDirectoryW((exeDir + L"\\Windows64").c_str(), NULL);
|
||||
CreateDirectoryW(gameHDDPath.c_str(), NULL);
|
||||
}
|
||||
}
|
||||
|
||||
// Set a callback for the default player options to be set - when there is no profile data for the player
|
||||
ProfileManager.SetDefaultOptionsCallback(&CConsoleMinecraftApp::DefaultOptionsCallback,(LPVOID)&app);
|
||||
#if 0
|
||||
@@ -1036,7 +1137,17 @@ int APIENTRY _tWinMain(_In_ HINSTANCE hInstance,
|
||||
// ProfileManager for XN_LIVE_INVITE_ACCEPTED for QNet.
|
||||
g_NetworkManager.Initialise();
|
||||
|
||||
for (int i = 0; i < MINECRAFT_NET_MAX_PLAYERS; i++)
|
||||
{
|
||||
IQNet::m_player[i].m_smallId = (BYTE)i;
|
||||
IQNet::m_player[i].m_isRemote = false;
|
||||
IQNet::m_player[i].m_isHostPlayer = (i == 0);
|
||||
swprintf_s(IQNet::m_player[i].m_gamertag, 32, L"Player%d", i);
|
||||
}
|
||||
extern wchar_t g_Win64UsernameW[17];
|
||||
wcscpy_s(IQNet::m_player[0].m_gamertag, 32, g_Win64UsernameW);
|
||||
|
||||
WinsockNetLayer::Initialize();
|
||||
|
||||
// 4J-PB moved further down
|
||||
//app.InitGameSettings();
|
||||
@@ -1135,6 +1246,7 @@ int APIENTRY _tWinMain(_In_ HINSTANCE hInstance,
|
||||
// Intro loop ?
|
||||
while(app.IntroRunning())
|
||||
{
|
||||
|
||||
ProfileManager.Tick();
|
||||
// Tick XUI
|
||||
app.RunFrame();
|
||||
@@ -1156,14 +1268,30 @@ int APIENTRY _tWinMain(_In_ HINSTANCE hInstance,
|
||||
MSG msg = {0};
|
||||
while( WM_QUIT != msg.message )
|
||||
{
|
||||
g_KBMInput.Tick();
|
||||
|
||||
if( PeekMessage( &msg, NULL, 0, 0, PM_REMOVE ) )
|
||||
while( PeekMessage( &msg, NULL, 0, 0, PM_REMOVE ) )
|
||||
{
|
||||
if( msg.message == WM_QUIT ) break;
|
||||
TranslateMessage( &msg );
|
||||
DispatchMessage( &msg );
|
||||
continue;
|
||||
}
|
||||
if( msg.message == WM_QUIT ) break;
|
||||
|
||||
g_KBMInput.Tick();
|
||||
|
||||
|
||||
|
||||
#ifdef _DEBUG
|
||||
for( int vk = 0; vk < 256; vk++ )
|
||||
{
|
||||
if( g_KBMInput.IsKeyPressed(vk) )
|
||||
{
|
||||
char dbgBuf[64];
|
||||
sprintf_s(dbgBuf, "INPUT: Key pressed vk=0x%02X\n", vk);
|
||||
OutputDebugStringA(dbgBuf);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
RenderManager.StartFrame();
|
||||
#if 0
|
||||
if(pMinecraft->soundEngine->isStreamingWavebankReady() &&
|
||||
@@ -1186,6 +1314,26 @@ int APIENTRY _tWinMain(_In_ HINSTANCE hInstance,
|
||||
PIXBeginNamedEvent(0,"Input manager tick");
|
||||
InputManager.Tick();
|
||||
PIXEndNamedEvent();
|
||||
|
||||
if (InputManager.IsPadConnected(0))
|
||||
{
|
||||
bool controllerActive = InputManager.ButtonPressed(0) ||
|
||||
InputManager.GetJoypadStick_LX(0,false) != 0.0f || InputManager.GetJoypadStick_LY(0,false) != 0.0f ||
|
||||
InputManager.GetJoypadStick_RX(0,false) != 0.0f || InputManager.GetJoypadStick_RY(0,false) != 0.0f ||
|
||||
InputManager.GetJoypadLTrigger(0,false) != 0 || InputManager.GetJoypadRTrigger(0,false) != 0;
|
||||
|
||||
if (controllerActive && g_KBMInput.IsKBMActive())
|
||||
{
|
||||
g_KBMInput.SetKBMActive(false);
|
||||
g_KBMInput.SetMouseGrabbed(false);
|
||||
g_KBMInput.SetCursorHiddenForUI(true);
|
||||
}
|
||||
else if (!g_KBMInput.IsKBMActive() && g_KBMInput.HasAnyInput())
|
||||
{
|
||||
g_KBMInput.SetCursorHiddenForUI(false);
|
||||
g_KBMInput.SetKBMActive(true);
|
||||
}
|
||||
}
|
||||
PIXBeginNamedEvent(0,"Profile manager tick");
|
||||
// ProfileManager.Tick();
|
||||
PIXEndNamedEvent();
|
||||
@@ -1209,9 +1357,11 @@ int APIENTRY _tWinMain(_In_ HINSTANCE hInstance,
|
||||
PIXEndNamedEvent();
|
||||
|
||||
PIXBeginNamedEvent(0,"Network manager do work #1");
|
||||
// g_NetworkManager.DoWork();
|
||||
g_NetworkManager.DoWork();
|
||||
PIXEndNamedEvent();
|
||||
|
||||
TickDiscord();
|
||||
|
||||
// LeaderboardManager::Instance()->Tick();
|
||||
// Render game graphics.
|
||||
if(app.GetGameStarted())
|
||||
|
||||
@@ -4,456 +4,482 @@
|
||||
#include <winhttp.h>
|
||||
#pragma comment(lib, "winhttp.lib")
|
||||
#include <shlobj.h>
|
||||
#include "../Windows64_Minecraft.h"
|
||||
#include "../User.h"
|
||||
|
||||
static bool ParseUrl(const wchar_t* url,
|
||||
std::wstring& host, INTERNET_PORT& port,
|
||||
std::wstring& path)
|
||||
std::wstring& host, INTERNET_PORT& port,
|
||||
std::wstring& path)
|
||||
{
|
||||
URL_COMPONENTS uc = {};
|
||||
uc.dwStructSize = sizeof(uc);
|
||||
uc.dwHostNameLength = (DWORD)-1;
|
||||
uc.dwUrlPathLength = (DWORD)-1;
|
||||
uc.dwExtraInfoLength = (DWORD)-1;
|
||||
if (!WinHttpCrackUrl(url, 0, 0, &uc)) return false;
|
||||
host.assign(uc.lpszHostName, uc.dwHostNameLength);
|
||||
path.assign(uc.lpszUrlPath, uc.dwUrlPathLength);
|
||||
if (uc.lpszExtraInfo && uc.dwExtraInfoLength)
|
||||
path.append(uc.lpszExtraInfo, uc.dwExtraInfoLength);
|
||||
port = uc.nPort;
|
||||
return true;
|
||||
URL_COMPONENTS uc = {};
|
||||
uc.dwStructSize = sizeof(uc);
|
||||
uc.dwHostNameLength = (DWORD)-1;
|
||||
uc.dwUrlPathLength = (DWORD)-1;
|
||||
uc.dwExtraInfoLength = (DWORD)-1;
|
||||
if (!WinHttpCrackUrl(url, 0, 0, &uc)) return false;
|
||||
host.assign(uc.lpszHostName, uc.dwHostNameLength);
|
||||
path.assign(uc.lpszUrlPath, uc.dwUrlPathLength);
|
||||
if (uc.lpszExtraInfo && uc.dwExtraInfoLength)
|
||||
path.append(uc.lpszExtraInfo, uc.dwExtraInfoLength);
|
||||
port = uc.nPort;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Windows64_DLCOffers::FetchBytesFromUrl(const wchar_t* url,
|
||||
PBYTE* ppData, DWORD* pdwSize)
|
||||
PBYTE* ppData, DWORD* pdwSize)
|
||||
{
|
||||
*ppData = nullptr;
|
||||
*pdwSize = 0;
|
||||
*ppData = nullptr;
|
||||
*pdwSize = 0;
|
||||
|
||||
printf("[DLC] FetchBytesFromUrl: '%ls'\n", url);
|
||||
printf("[DLC] FetchBytesFromUrl: '%ls'\n", url);
|
||||
|
||||
std::wstring host, path;
|
||||
INTERNET_PORT port = 80;
|
||||
if (!ParseUrl(url, host, port, path))
|
||||
{
|
||||
printf("[DLC] FetchBytesFromUrl: ParseUrl FAILED (err=%u)\n", GetLastError());
|
||||
return false;
|
||||
}
|
||||
std::wstring host, path;
|
||||
INTERNET_PORT port = 80;
|
||||
if (!ParseUrl(url, host, port, path))
|
||||
{
|
||||
printf("[DLC] FetchBytesFromUrl: ParseUrl FAILED (err=%u)\n", GetLastError());
|
||||
return false;
|
||||
}
|
||||
|
||||
printf("[DLC] FetchBytesFromUrl: host='%ls' port=%u path='%ls'\n",
|
||||
host.c_str(), (unsigned)port, path.c_str());
|
||||
printf("[DLC] FetchBytesFromUrl: host='%ls' port=%u path='%ls'\n",
|
||||
host.c_str(), (unsigned)port, path.c_str());
|
||||
|
||||
HINTERNET hSession = WinHttpOpen(L"W64DLC/1.0",
|
||||
WINHTTP_ACCESS_TYPE_NO_PROXY,
|
||||
WINHTTP_NO_PROXY_NAME,
|
||||
WINHTTP_NO_PROXY_BYPASS, 0);
|
||||
if (!hSession)
|
||||
{
|
||||
printf("[DLC] FetchBytesFromUrl: WinHttpOpen FAILED (err=%u)\n", GetLastError());
|
||||
return false;
|
||||
}
|
||||
HINTERNET hSession = WinHttpOpen(L"W64DLC/1.0",
|
||||
WINHTTP_ACCESS_TYPE_NO_PROXY,
|
||||
WINHTTP_NO_PROXY_NAME,
|
||||
WINHTTP_NO_PROXY_BYPASS, 0);
|
||||
if (!hSession)
|
||||
{
|
||||
printf("[DLC] FetchBytesFromUrl: WinHttpOpen FAILED (err=%u)\n", GetLastError());
|
||||
return false;
|
||||
}
|
||||
|
||||
HINTERNET hConn = WinHttpConnect(hSession, host.c_str(), port, 0);
|
||||
if (!hConn)
|
||||
{
|
||||
printf("[DLC] FetchBytesFromUrl: WinHttpConnect FAILED (err=%u)\n", GetLastError());
|
||||
WinHttpCloseHandle(hSession);
|
||||
return false;
|
||||
}
|
||||
HINTERNET hConn = WinHttpConnect(hSession, host.c_str(), port, 0);
|
||||
if (!hConn)
|
||||
{
|
||||
printf("[DLC] FetchBytesFromUrl: WinHttpConnect FAILED (err=%u)\n", GetLastError());
|
||||
WinHttpCloseHandle(hSession);
|
||||
return false;
|
||||
}
|
||||
|
||||
HINTERNET hReq = WinHttpOpenRequest(hConn, L"GET", path.c_str(),
|
||||
nullptr, WINHTTP_NO_REFERER,
|
||||
WINHTTP_DEFAULT_ACCEPT_TYPES, 0);
|
||||
if (!hReq)
|
||||
{
|
||||
printf("[DLC] FetchBytesFromUrl: WinHttpOpenRequest FAILED (err=%u)\n", GetLastError());
|
||||
WinHttpCloseHandle(hConn);
|
||||
WinHttpCloseHandle(hSession);
|
||||
return false;
|
||||
}
|
||||
HINTERNET hReq = WinHttpOpenRequest(hConn, L"GET", path.c_str(),
|
||||
nullptr, WINHTTP_NO_REFERER,
|
||||
WINHTTP_DEFAULT_ACCEPT_TYPES, 0);
|
||||
if (!hReq)
|
||||
{
|
||||
printf("[DLC] FetchBytesFromUrl: WinHttpOpenRequest FAILED (err=%u)\n", GetLastError());
|
||||
WinHttpCloseHandle(hConn);
|
||||
WinHttpCloseHandle(hSession);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!WinHttpSendRequest(hReq, WINHTTP_NO_ADDITIONAL_HEADERS, 0,
|
||||
WINHTTP_NO_REQUEST_DATA, 0, 0, 0))
|
||||
{
|
||||
printf("[DLC] FetchBytesFromUrl: WinHttpSendRequest FAILED (err=%u)\n", GetLastError());
|
||||
WinHttpCloseHandle(hReq);
|
||||
WinHttpCloseHandle(hConn);
|
||||
WinHttpCloseHandle(hSession);
|
||||
return false;
|
||||
}
|
||||
if (!WinHttpSendRequest(hReq, WINHTTP_NO_ADDITIONAL_HEADERS, 0,
|
||||
WINHTTP_NO_REQUEST_DATA, 0, 0, 0))
|
||||
{
|
||||
printf("[DLC] FetchBytesFromUrl: WinHttpSendRequest FAILED (err=%u)\n", GetLastError());
|
||||
WinHttpCloseHandle(hReq);
|
||||
WinHttpCloseHandle(hConn);
|
||||
WinHttpCloseHandle(hSession);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!WinHttpReceiveResponse(hReq, nullptr))
|
||||
{
|
||||
printf("[DLC] FetchBytesFromUrl: WinHttpReceiveResponse FAILED (err=%u)\n", GetLastError());
|
||||
WinHttpCloseHandle(hReq);
|
||||
WinHttpCloseHandle(hConn);
|
||||
WinHttpCloseHandle(hSession);
|
||||
return false;
|
||||
}
|
||||
if (!WinHttpReceiveResponse(hReq, nullptr))
|
||||
{
|
||||
printf("[DLC] FetchBytesFromUrl: WinHttpReceiveResponse FAILED (err=%u)\n", GetLastError());
|
||||
WinHttpCloseHandle(hReq);
|
||||
WinHttpCloseHandle(hConn);
|
||||
WinHttpCloseHandle(hSession);
|
||||
return false;
|
||||
}
|
||||
|
||||
DWORD dwStatusCode = 0;
|
||||
DWORD dwStatusSize = sizeof(dwStatusCode);
|
||||
WinHttpQueryHeaders(hReq,
|
||||
WINHTTP_QUERY_STATUS_CODE | WINHTTP_QUERY_FLAG_NUMBER,
|
||||
WINHTTP_HEADER_NAME_BY_INDEX,
|
||||
&dwStatusCode, &dwStatusSize,
|
||||
WINHTTP_NO_HEADER_INDEX);
|
||||
printf("[DLC] FetchBytesFromUrl: HTTP status = %u\n", dwStatusCode);
|
||||
DWORD dwStatusCode = 0;
|
||||
DWORD dwStatusSize = sizeof(dwStatusCode);
|
||||
WinHttpQueryHeaders(hReq,
|
||||
WINHTTP_QUERY_STATUS_CODE | WINHTTP_QUERY_FLAG_NUMBER,
|
||||
WINHTTP_HEADER_NAME_BY_INDEX,
|
||||
&dwStatusCode, &dwStatusSize,
|
||||
WINHTTP_NO_HEADER_INDEX);
|
||||
printf("[DLC] FetchBytesFromUrl: HTTP status = %u\n", dwStatusCode);
|
||||
|
||||
if (dwStatusCode != 200)
|
||||
{
|
||||
printf("[DLC] FetchBytesFromUrl: non-200 response, aborting\n");
|
||||
WinHttpCloseHandle(hReq);
|
||||
WinHttpCloseHandle(hConn);
|
||||
WinHttpCloseHandle(hSession);
|
||||
return false;
|
||||
}
|
||||
if (dwStatusCode != 200)
|
||||
{
|
||||
printf("[DLC] FetchBytesFromUrl: non-200 response, aborting\n");
|
||||
WinHttpCloseHandle(hReq);
|
||||
WinHttpCloseHandle(hConn);
|
||||
WinHttpCloseHandle(hSession);
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<BYTE> buf;
|
||||
DWORD dwRead = 0;
|
||||
BYTE chunk[8192];
|
||||
while (WinHttpReadData(hReq, chunk, sizeof(chunk), &dwRead) && dwRead)
|
||||
buf.insert(buf.end(), chunk, chunk + dwRead);
|
||||
std::vector<BYTE> buf;
|
||||
DWORD dwRead = 0;
|
||||
BYTE chunk[8192];
|
||||
while (WinHttpReadData(hReq, chunk, sizeof(chunk), &dwRead) && dwRead)
|
||||
buf.insert(buf.end(), chunk, chunk + dwRead);
|
||||
|
||||
printf("[DLC] FetchBytesFromUrl: read %zu bytes\n", buf.size());
|
||||
printf("[DLC] FetchBytesFromUrl: read %zu bytes\n", buf.size());
|
||||
|
||||
bool bOk = false;
|
||||
if (!buf.empty())
|
||||
{
|
||||
*ppData = new BYTE[buf.size()];
|
||||
*pdwSize = (DWORD)buf.size();
|
||||
memcpy(*ppData, buf.data(), buf.size());
|
||||
bOk = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("[DLC] FetchBytesFromUrl: response body was empty\n");
|
||||
}
|
||||
bool bOk = false;
|
||||
if (!buf.empty())
|
||||
{
|
||||
*ppData = new BYTE[buf.size()];
|
||||
*pdwSize = (DWORD)buf.size();
|
||||
memcpy(*ppData, buf.data(), buf.size());
|
||||
bOk = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("[DLC] FetchBytesFromUrl: response body was empty\n");
|
||||
}
|
||||
|
||||
WinHttpCloseHandle(hReq);
|
||||
WinHttpCloseHandle(hConn);
|
||||
WinHttpCloseHandle(hSession);
|
||||
return bOk;
|
||||
WinHttpCloseHandle(hReq);
|
||||
WinHttpCloseHandle(hConn);
|
||||
WinHttpCloseHandle(hSession);
|
||||
return bOk;
|
||||
}
|
||||
|
||||
static bool WinHttpGetUrl(const wchar_t* fullUrl, PBYTE* ppData, DWORD* pdwSize)
|
||||
{
|
||||
*ppData = nullptr;
|
||||
*pdwSize = 0;
|
||||
*ppData = nullptr;
|
||||
*pdwSize = 0;
|
||||
|
||||
URL_COMPONENTS uc;
|
||||
ZeroMemory(&uc, sizeof(uc));
|
||||
uc.dwStructSize = sizeof(uc);
|
||||
URL_COMPONENTS uc;
|
||||
ZeroMemory(&uc, sizeof(uc));
|
||||
uc.dwStructSize = sizeof(uc);
|
||||
|
||||
wchar_t szHostName[256] = {};
|
||||
wchar_t szUrlPath[1024] = {};
|
||||
uc.lpszHostName = szHostName;
|
||||
uc.dwHostNameLength = _countof(szHostName);
|
||||
uc.lpszUrlPath = szUrlPath;
|
||||
uc.dwUrlPathLength = _countof(szUrlPath);
|
||||
wchar_t szHostName[256] = {};
|
||||
wchar_t szUrlPath[1024] = {};
|
||||
uc.lpszHostName = szHostName;
|
||||
uc.dwHostNameLength = _countof(szHostName);
|
||||
uc.lpszUrlPath = szUrlPath;
|
||||
uc.dwUrlPathLength = _countof(szUrlPath);
|
||||
|
||||
if (!WinHttpCrackUrl(fullUrl, 0, 0, &uc))
|
||||
{
|
||||
printf("[DLC] WinHttpGetUrl: WinHttpCrackUrl failed for '%ls': %u\n", fullUrl, GetLastError());
|
||||
return false;
|
||||
}
|
||||
if (!WinHttpCrackUrl(fullUrl, 0, 0, &uc))
|
||||
{
|
||||
printf("[DLC] WinHttpGetUrl: WinHttpCrackUrl failed for '%ls': %u\n", fullUrl, GetLastError());
|
||||
return false;
|
||||
}
|
||||
|
||||
HINTERNET hSession = WinHttpOpen(L"MC/1.0", WINHTTP_ACCESS_TYPE_NO_PROXY, NULL, NULL, 0);
|
||||
if (!hSession) return false;
|
||||
HINTERNET hSession = WinHttpOpen(L"MC/1.0", WINHTTP_ACCESS_TYPE_NO_PROXY, NULL, NULL, 0);
|
||||
if (!hSession) return false;
|
||||
|
||||
HINTERNET hConnect = WinHttpConnect(hSession, szHostName, uc.nPort, 0);
|
||||
if (!hConnect) { WinHttpCloseHandle(hSession); return false; }
|
||||
HINTERNET hConnect = WinHttpConnect(hSession, szHostName, uc.nPort, 0);
|
||||
if (!hConnect) { WinHttpCloseHandle(hSession); return false; }
|
||||
|
||||
HINTERNET hRequest = WinHttpOpenRequest(hConnect, L"GET", szUrlPath,
|
||||
NULL, WINHTTP_NO_REFERER, WINHTTP_DEFAULT_ACCEPT_TYPES, 0);
|
||||
if (!hRequest)
|
||||
{
|
||||
WinHttpCloseHandle(hConnect);
|
||||
WinHttpCloseHandle(hSession);
|
||||
return false;
|
||||
}
|
||||
HINTERNET hRequest = WinHttpOpenRequest(hConnect, L"GET", szUrlPath,
|
||||
NULL, WINHTTP_NO_REFERER, WINHTTP_DEFAULT_ACCEPT_TYPES, 0);
|
||||
if (!hRequest)
|
||||
{
|
||||
WinHttpCloseHandle(hConnect);
|
||||
WinHttpCloseHandle(hSession);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!WinHttpSendRequest(hRequest, WINHTTP_NO_ADDITIONAL_HEADERS, 0,
|
||||
WINHTTP_NO_REQUEST_DATA, 0, 0, 0) ||
|
||||
!WinHttpReceiveResponse(hRequest, NULL))
|
||||
{
|
||||
WinHttpCloseHandle(hRequest);
|
||||
WinHttpCloseHandle(hConnect);
|
||||
WinHttpCloseHandle(hSession);
|
||||
return false;
|
||||
}
|
||||
if (!WinHttpSendRequest(hRequest, WINHTTP_NO_ADDITIONAL_HEADERS, 0,
|
||||
WINHTTP_NO_REQUEST_DATA, 0, 0, 0) ||
|
||||
!WinHttpReceiveResponse(hRequest, NULL))
|
||||
{
|
||||
WinHttpCloseHandle(hRequest);
|
||||
WinHttpCloseHandle(hConnect);
|
||||
WinHttpCloseHandle(hSession);
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string body;
|
||||
DWORD dwSize = 0;
|
||||
do {
|
||||
DWORD dwDownloaded = 0;
|
||||
if (!WinHttpQueryDataAvailable(hRequest, &dwSize) || dwSize == 0) break;
|
||||
char* buf = new char[dwSize];
|
||||
if (WinHttpReadData(hRequest, buf, dwSize, &dwDownloaded))
|
||||
body.append(buf, dwDownloaded);
|
||||
delete[] buf;
|
||||
} while (dwSize > 0);
|
||||
std::string body;
|
||||
DWORD dwSize = 0;
|
||||
do {
|
||||
DWORD dwDownloaded = 0;
|
||||
if (!WinHttpQueryDataAvailable(hRequest, &dwSize) || dwSize == 0) break;
|
||||
char* buf = new char[dwSize];
|
||||
if (WinHttpReadData(hRequest, buf, dwSize, &dwDownloaded))
|
||||
body.append(buf, dwDownloaded);
|
||||
delete[] buf;
|
||||
} while (dwSize > 0);
|
||||
|
||||
WinHttpCloseHandle(hRequest);
|
||||
WinHttpCloseHandle(hConnect);
|
||||
WinHttpCloseHandle(hSession);
|
||||
WinHttpCloseHandle(hRequest);
|
||||
WinHttpCloseHandle(hConnect);
|
||||
WinHttpCloseHandle(hSession);
|
||||
|
||||
if (body.empty()) return false;
|
||||
if (body.empty()) return false;
|
||||
|
||||
*pdwSize = (DWORD)body.size();
|
||||
*ppData = new BYTE[*pdwSize];
|
||||
memcpy(*ppData, body.data(), *pdwSize);
|
||||
return true;
|
||||
*pdwSize = (DWORD)body.size();
|
||||
*ppData = new BYTE[*pdwSize];
|
||||
memcpy(*ppData, body.data(), *pdwSize);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Windows64_DLCOffers::FetchImageFromUrl(const wchar_t* url, PBYTE* ppData, DWORD* pdwSize)
|
||||
{
|
||||
return WinHttpGetUrl(url, ppData, pdwSize);
|
||||
return WinHttpGetUrl(url, ppData, pdwSize);
|
||||
}
|
||||
|
||||
void Windows64_DLCOffers::InstallOffer(int iIndex,
|
||||
W64_INSTALL_CALLBACK pfnCallback,
|
||||
void* pUserData)
|
||||
W64_INSTALL_CALLBACK pfnCallback,
|
||||
void* pUserData, wstring username)
|
||||
{
|
||||
if (iIndex < 0 || iIndex >= (int)m_offers.size()) return;
|
||||
if (iIndex < 0 || iIndex >= (int)m_offers.size()) return;
|
||||
|
||||
InstallCtx* ctx = new InstallCtx();
|
||||
ctx->offer = m_offers[iIndex];
|
||||
ctx->offerIndex = iIndex;
|
||||
ctx->pfnCallback = pfnCallback;
|
||||
ctx->pUserData = pUserData;
|
||||
InstallCtx* ctx = new InstallCtx();
|
||||
ctx->offer = m_offers[iIndex];
|
||||
ctx->offerIndex = iIndex;
|
||||
ctx->pfnCallback = pfnCallback;
|
||||
ctx->pUserData = pUserData;
|
||||
ctx->username = username;
|
||||
|
||||
HANDLE hThread = CreateThread(nullptr, 0, InstallThreadProc, ctx, 0, nullptr);
|
||||
if (hThread) CloseHandle(hThread);
|
||||
else delete ctx;
|
||||
HANDLE hThread = CreateThread(nullptr, 0, InstallThreadProc, ctx, 0, nullptr);
|
||||
if (hThread) CloseHandle(hThread);
|
||||
else delete ctx;
|
||||
}
|
||||
|
||||
DWORD WINAPI Windows64_DLCOffers::InstallThreadProc(LPVOID lpParam)
|
||||
{
|
||||
InstallCtx* ctx = reinterpret_cast<InstallCtx*>(lpParam);
|
||||
bool bSuccess = false;
|
||||
InstallCtx* ctx = reinterpret_cast<InstallCtx*>(lpParam);
|
||||
bool bSuccess = false;
|
||||
|
||||
// <exe dir>\Windows64Media\DLC\<productID>\folder.pck
|
||||
wchar_t wszExeDir[MAX_PATH] = {};
|
||||
GetModuleFileNameW(nullptr, wszExeDir, MAX_PATH);
|
||||
wchar_t* pLastSlash = wcsrchr(wszExeDir, L'\\');
|
||||
if (pLastSlash) *(pLastSlash + 1) = L'\0';
|
||||
// <exe dir>\Windows64Media\DLC\<productID>\folder.pck
|
||||
wchar_t wszExeDir[MAX_PATH] = {};
|
||||
GetModuleFileNameW(nullptr, wszExeDir, MAX_PATH);
|
||||
wchar_t* pLastSlash = wcsrchr(wszExeDir, L'\\');
|
||||
if (pLastSlash) *(pLastSlash + 1) = L'\0';
|
||||
|
||||
wchar_t wszInstallDir[MAX_PATH];
|
||||
_snwprintf_s(wszInstallDir, _countof(wszInstallDir), _TRUNCATE,
|
||||
L"%lsWindows64Media\\DLC\\%ls",
|
||||
wszExeDir, ctx->offer.wszProductID);
|
||||
wchar_t wszInstallDir[MAX_PATH];
|
||||
_snwprintf_s(wszInstallDir, _countof(wszInstallDir), _TRUNCATE,
|
||||
L"%lsWindows64Media\\DLC\\%ls",
|
||||
wszExeDir, ctx->offer.wszProductID);
|
||||
|
||||
CreateDirectoryW(wszInstallDir, nullptr);
|
||||
CreateDirectoryW(wszInstallDir, nullptr);
|
||||
|
||||
wchar_t wszFilePath[MAX_PATH];
|
||||
_snwprintf_s(wszFilePath, _countof(wszFilePath), _TRUNCATE,
|
||||
L"%ls\\folder.pck", wszInstallDir);
|
||||
wchar_t wszFilePath[MAX_PATH];
|
||||
_snwprintf_s(wszFilePath, _countof(wszFilePath), _TRUNCATE,
|
||||
L"%ls\\folder.pck", wszInstallDir);
|
||||
|
||||
wchar_t wszDownloadUrl[512];
|
||||
_snwprintf_s(wszDownloadUrl, _countof(wszDownloadUrl), _TRUNCATE,
|
||||
L"http://127.0.0.1:3000/download/%ls", ctx->offer.wszProductID);
|
||||
wchar_t wszDownloadUrl[512];
|
||||
_snwprintf_s(wszDownloadUrl, _countof(wszDownloadUrl), _TRUNCATE,
|
||||
L"http://127.0.0.1:3000/download/%ls", ctx->offer.wszProductID);
|
||||
|
||||
PBYTE pData = nullptr;
|
||||
DWORD dwBytes = 0;
|
||||
PBYTE pData = nullptr;
|
||||
DWORD dwBytes = 0;
|
||||
|
||||
if (FetchBytesFromUrl(wszDownloadUrl, &pData, &dwBytes) && pData)
|
||||
{
|
||||
HANDLE hFile = CreateFileW(wszFilePath, GENERIC_WRITE, 0, nullptr,
|
||||
CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);
|
||||
if (hFile != INVALID_HANDLE_VALUE)
|
||||
{
|
||||
DWORD dwWritten = 0;
|
||||
WriteFile(hFile, pData, dwBytes, &dwWritten, nullptr);
|
||||
CloseHandle(hFile);
|
||||
bSuccess = (dwWritten == dwBytes);
|
||||
}
|
||||
delete[] pData;
|
||||
}
|
||||
if (FetchBytesFromUrl(wszDownloadUrl, &pData, &dwBytes) && pData)
|
||||
{
|
||||
HANDLE hFile = CreateFileW(wszFilePath, GENERIC_WRITE, 0, nullptr,
|
||||
CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);
|
||||
if (hFile != INVALID_HANDLE_VALUE)
|
||||
{
|
||||
DWORD dwWritten = 0;
|
||||
WriteFile(hFile, pData, dwBytes, &dwWritten, nullptr);
|
||||
CloseHandle(hFile);
|
||||
bSuccess = (dwWritten == dwBytes);
|
||||
}
|
||||
delete[] pData;
|
||||
}
|
||||
|
||||
if (bSuccess)
|
||||
{
|
||||
// Mark the offer as owned.
|
||||
Windows64_DLCOffers::Get().SetPurchased(ctx->offerIndex);
|
||||
if (bSuccess)
|
||||
{
|
||||
Windows64_DLCOffers::Get().SetPurchased(ctx->offerIndex);
|
||||
Windows64_DLCOffers::Get().SetPendingLoadPath(ctx->offerIndex, wszFilePath);
|
||||
InterlockedExchange(&Windows64_DLCOffers::Get().m_iLastInstalled, (LONG)ctx->offerIndex);
|
||||
|
||||
// Must be set BEFORE m_iLastInstalled is updated so the game thread always
|
||||
Windows64_DLCOffers::Get().SetPendingLoadPath(ctx->offerIndex, wszFilePath);
|
||||
|
||||
InterlockedExchange(&Windows64_DLCOffers::Get().m_iLastInstalled,
|
||||
(LONG)ctx->offerIndex);
|
||||
// notify server so purchase persists across sessions
|
||||
wchar_t wszPath[256];
|
||||
_snwprintf_s(wszPath, _countof(wszPath), _TRUNCATE,
|
||||
L"/purchase/%ls?user=%ls",
|
||||
ctx->offer.wszProductID, ctx->username.c_str());
|
||||
|
||||
printf("[DLC] Installed '%ls' -> %ls\n",
|
||||
ctx->offer.wszProductID, wszFilePath);
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("[DLC] Install FAILED for '%ls'\n", ctx->offer.wszProductID);
|
||||
}
|
||||
HINTERNET hSession = WinHttpOpen(L"W64DLC/1.0", WINHTTP_ACCESS_TYPE_NO_PROXY, nullptr, nullptr, 0);
|
||||
if (hSession)
|
||||
{
|
||||
HINTERNET hConn = WinHttpConnect(hSession, L"127.0.0.1", 3000, 0);
|
||||
if (hConn)
|
||||
{
|
||||
HINTERNET hReq = WinHttpOpenRequest(hConn, L"POST", wszPath, nullptr,
|
||||
WINHTTP_NO_REFERER,
|
||||
WINHTTP_DEFAULT_ACCEPT_TYPES, 0);
|
||||
if (hReq)
|
||||
{
|
||||
WinHttpSendRequest(hReq, WINHTTP_NO_ADDITIONAL_HEADERS, 0,
|
||||
WINHTTP_NO_REQUEST_DATA, 0, 0, 0);
|
||||
WinHttpReceiveResponse(hReq, nullptr);
|
||||
WinHttpCloseHandle(hReq);
|
||||
}
|
||||
WinHttpCloseHandle(hConn);
|
||||
}
|
||||
WinHttpCloseHandle(hSession);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("[DLC] Install FAILED for '%ls'\n", ctx->offer.wszProductID);
|
||||
}
|
||||
|
||||
if (ctx->pfnCallback)
|
||||
ctx->pfnCallback(ctx->offer.wszProductID, bSuccess, ctx->pUserData);
|
||||
if (ctx->pfnCallback)
|
||||
ctx->pfnCallback(ctx->offer.wszProductID, bSuccess, ctx->pUserData);
|
||||
|
||||
delete ctx;
|
||||
return 0;
|
||||
delete ctx;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Windows64_DLCOffers::FetchFromServer()
|
||||
{
|
||||
printf("[DLC] FetchFromServer called\n");
|
||||
m_offers.clear();
|
||||
m_bReady = false;
|
||||
printf("[DLC] FetchFromServer called\n");
|
||||
m_offers.clear();
|
||||
m_bReady = false;
|
||||
|
||||
HINTERNET hSession = WinHttpOpen(L"MC/1.0", WINHTTP_ACCESS_TYPE_NO_PROXY, NULL, NULL, 0);
|
||||
if (!hSession) return;
|
||||
HINTERNET hSession = WinHttpOpen(L"MC/1.0", WINHTTP_ACCESS_TYPE_NO_PROXY, NULL, NULL, 0);
|
||||
if (!hSession) return;
|
||||
|
||||
HINTERNET hConnect = WinHttpConnect(hSession, L"localhost", 3000, 0);
|
||||
if (!hConnect) { WinHttpCloseHandle(hSession); return; }
|
||||
HINTERNET hConnect = WinHttpConnect(hSession, L"localhost", 3000, 0);
|
||||
if (!hConnect) { WinHttpCloseHandle(hSession); return; }
|
||||
|
||||
HINTERNET hRequest = WinHttpOpenRequest(hConnect, L"GET", L"/dlc",
|
||||
NULL, WINHTTP_NO_REFERER, WINHTTP_DEFAULT_ACCEPT_TYPES, 0);
|
||||
if (!hRequest)
|
||||
{
|
||||
WinHttpCloseHandle(hConnect);
|
||||
WinHttpCloseHandle(hSession);
|
||||
return;
|
||||
}
|
||||
wchar_t wszDlcPath[128];
|
||||
_snwprintf_s(wszDlcPath, _countof(wszDlcPath), _TRUNCATE, L"/dlc?user=%ls", Minecraft::GetInstance()->user->name.c_str());
|
||||
|
||||
if (!WinHttpSendRequest(hRequest, WINHTTP_NO_ADDITIONAL_HEADERS, 0,
|
||||
WINHTTP_NO_REQUEST_DATA, 0, 0, 0) ||
|
||||
!WinHttpReceiveResponse(hRequest, NULL))
|
||||
{
|
||||
WinHttpCloseHandle(hRequest);
|
||||
WinHttpCloseHandle(hConnect);
|
||||
WinHttpCloseHandle(hSession);
|
||||
return;
|
||||
}
|
||||
HINTERNET hRequest = WinHttpOpenRequest(hConnect, L"GET", wszDlcPath,
|
||||
NULL, WINHTTP_NO_REFERER, WINHTTP_DEFAULT_ACCEPT_TYPES, 0);
|
||||
if (!hRequest)
|
||||
{
|
||||
WinHttpCloseHandle(hConnect);
|
||||
WinHttpCloseHandle(hSession);
|
||||
return;
|
||||
}
|
||||
|
||||
std::string body;
|
||||
DWORD dwSize = 0;
|
||||
do {
|
||||
DWORD dwDownloaded = 0;
|
||||
if (!WinHttpQueryDataAvailable(hRequest, &dwSize) || dwSize == 0) break;
|
||||
char* buf = new char[dwSize + 1];
|
||||
ZeroMemory(buf, dwSize + 1);
|
||||
if (WinHttpReadData(hRequest, buf, dwSize, &dwDownloaded))
|
||||
body.append(buf, dwDownloaded);
|
||||
delete[] buf;
|
||||
} while (dwSize > 0);
|
||||
if (!WinHttpSendRequest(hRequest, WINHTTP_NO_ADDITIONAL_HEADERS, 0,
|
||||
WINHTTP_NO_REQUEST_DATA, 0, 0, 0) ||
|
||||
!WinHttpReceiveResponse(hRequest, NULL))
|
||||
{
|
||||
WinHttpCloseHandle(hRequest);
|
||||
WinHttpCloseHandle(hConnect);
|
||||
WinHttpCloseHandle(hSession);
|
||||
return;
|
||||
}
|
||||
|
||||
WinHttpCloseHandle(hRequest);
|
||||
WinHttpCloseHandle(hConnect);
|
||||
WinHttpCloseHandle(hSession);
|
||||
std::string body;
|
||||
DWORD dwSize = 0;
|
||||
do {
|
||||
DWORD dwDownloaded = 0;
|
||||
if (!WinHttpQueryDataAvailable(hRequest, &dwSize) || dwSize == 0) break;
|
||||
char* buf = new char[dwSize + 1];
|
||||
ZeroMemory(buf, dwSize + 1);
|
||||
if (WinHttpReadData(hRequest, buf, dwSize, &dwDownloaded))
|
||||
body.append(buf, dwDownloaded);
|
||||
delete[] buf;
|
||||
} while (dwSize > 0);
|
||||
|
||||
if (body.empty()) { printf("[DLC] Empty /dlc response\n"); return; }
|
||||
WinHttpCloseHandle(hRequest);
|
||||
WinHttpCloseHandle(hConnect);
|
||||
WinHttpCloseHandle(hSession);
|
||||
|
||||
printf("[DLC] /dlc response (%d bytes): %s\n", (int)body.size(), body.c_str());
|
||||
if (body.empty()) { printf("[DLC] Empty /dlc response\n"); return; }
|
||||
|
||||
auto toWide = [](const std::string& s, wchar_t* dst, int maxChars) {
|
||||
MultiByteToWideChar(CP_UTF8, 0, s.c_str(), -1, dst, maxChars);
|
||||
};
|
||||
printf("[DLC] /dlc response (%d bytes): %s\n", (int)body.size(), body.c_str());
|
||||
|
||||
size_t pos = 0;
|
||||
while ((pos = body.find('{', pos)) != std::string::npos)
|
||||
{
|
||||
W64_OFFER_INFO offer;
|
||||
ZeroMemory(&offer, sizeof(offer));
|
||||
auto toWide = [](const std::string& s, wchar_t* dst, int maxChars) {
|
||||
MultiByteToWideChar(CP_UTF8, 0, s.c_str(), -1, dst, maxChars);
|
||||
};
|
||||
|
||||
auto readField = [&](const char* key) -> std::string {
|
||||
std::string search = std::string("\"") + key + "\"";
|
||||
size_t k = body.find(search, pos);
|
||||
if (k == std::string::npos) return "";
|
||||
size_t colon = body.find(':', k);
|
||||
if (colon == std::string::npos) return "";
|
||||
size_t q1 = body.find('"', colon);
|
||||
if (q1 == std::string::npos) return "";
|
||||
size_t q2 = body.find('"', q1 + 1);
|
||||
if (q2 == std::string::npos) return "";
|
||||
return body.substr(q1 + 1, q2 - q1 - 1);
|
||||
};
|
||||
size_t pos = 0;
|
||||
while ((pos = body.find('{', pos)) != std::string::npos)
|
||||
{
|
||||
W64_OFFER_INFO offer;
|
||||
ZeroMemory(&offer, sizeof(offer));
|
||||
|
||||
auto readInt = [&](const char* key) -> int {
|
||||
std::string search = std::string("\"") + key + "\"";
|
||||
size_t k = body.find(search, pos);
|
||||
if (k == std::string::npos) return 0;
|
||||
size_t colon = body.find(':', k);
|
||||
if (colon == std::string::npos) return 0;
|
||||
size_t n = colon + 1;
|
||||
while (n < body.size() && (body[n] == ' ' || body[n] == '\t')) n++;
|
||||
return atoi(&body[n]);
|
||||
};
|
||||
auto readField = [&](const char* key) -> std::string {
|
||||
std::string search = std::string("\"") + key + "\"";
|
||||
size_t k = body.find(search, pos);
|
||||
if (k == std::string::npos) return "";
|
||||
size_t colon = body.find(':', k);
|
||||
if (colon == std::string::npos) return "";
|
||||
size_t q1 = body.find('"', colon);
|
||||
if (q1 == std::string::npos) return "";
|
||||
size_t q2 = body.find('"', q1 + 1);
|
||||
if (q2 == std::string::npos) return "";
|
||||
return body.substr(q1 + 1, q2 - q1 - 1);
|
||||
};
|
||||
|
||||
toWide(readField("id"), offer.wszProductID, 64);
|
||||
toWide(readField("name"), offer.wszOfferName, 128);
|
||||
toWide(readField("description"), offer.wszSellText, 512);
|
||||
toWide(readField("price"), offer.wszCurrencyPrice, 32);
|
||||
toWide(readField("type"), offer.wszType, 32);
|
||||
toWide(readField("bannerUrl"), offer.wszBannerUrl, 256);
|
||||
offer.fUserHasPurchased = readInt("purchased");
|
||||
offer.pbBannerData = nullptr;
|
||||
offer.dwBannerBytes = 0;
|
||||
auto readInt = [&](const char* key) -> int {
|
||||
std::string search = std::string("\"") + key + "\"";
|
||||
size_t k = body.find(search, pos);
|
||||
if (k == std::string::npos) return 0;
|
||||
size_t colon = body.find(':', k);
|
||||
if (colon == std::string::npos) return 0;
|
||||
size_t n = colon + 1;
|
||||
while (n < body.size() && (body[n] == ' ' || body[n] == '\t')) n++;
|
||||
return atoi(&body[n]);
|
||||
};
|
||||
|
||||
if (offer.wszProductID[0] != L'\0')
|
||||
{
|
||||
printf("[DLC] Parsed: id='%ls' type='%ls' bannerUrl='%ls'\n",
|
||||
offer.wszProductID, offer.wszType, offer.wszBannerUrl);
|
||||
m_offers.push_back(offer);
|
||||
}
|
||||
toWide(readField("id"), offer.wszProductID, 64);
|
||||
toWide(readField("name"), offer.wszOfferName, 128);
|
||||
toWide(readField("description"), offer.wszSellText, 512);
|
||||
toWide(readField("price"), offer.wszCurrencyPrice, 32);
|
||||
toWide(readField("type"), offer.wszType, 32);
|
||||
toWide(readField("bannerUrl"), offer.wszBannerUrl, 256);
|
||||
offer.fUserHasPurchased = readInt("purchased");
|
||||
offer.pbBannerData = nullptr;
|
||||
offer.dwBannerBytes = 0;
|
||||
|
||||
pos = body.find('}', pos);
|
||||
if (pos == std::string::npos) break;
|
||||
pos++;
|
||||
}
|
||||
if (offer.wszProductID[0] != L'\0')
|
||||
{
|
||||
printf("[DLC] Parsed: id='%ls' type='%ls' bannerUrl='%ls'\n",
|
||||
offer.wszProductID, offer.wszType, offer.wszBannerUrl);
|
||||
m_offers.push_back(offer);
|
||||
}
|
||||
|
||||
printf("[DLC] FetchFromServer complete - %d offers\n", (int)m_offers.size());
|
||||
m_bReady = true;
|
||||
pos = body.find('}', pos);
|
||||
if (pos == std::string::npos) break;
|
||||
pos++;
|
||||
}
|
||||
|
||||
printf("[DLC] FetchFromServer complete - %d offers\n", (int)m_offers.size());
|
||||
m_bReady = true;
|
||||
}
|
||||
|
||||
void Windows64_DLCOffers::FetchBanners()
|
||||
{
|
||||
for (int i = 0; i < (int)m_offers.size(); i++)
|
||||
{
|
||||
W64_OFFER_INFO& offer = m_offers[i];
|
||||
if (offer.wszBannerUrl[0] == L'\0') continue;
|
||||
if (offer.pbBannerData != nullptr) continue;
|
||||
for (int i = 0; i < (int)m_offers.size(); i++)
|
||||
{
|
||||
W64_OFFER_INFO& offer = m_offers[i];
|
||||
if (offer.wszBannerUrl[0] == L'\0') continue;
|
||||
if (offer.pbBannerData != nullptr) continue;
|
||||
|
||||
PBYTE pbData = nullptr;
|
||||
DWORD dwBytes = 0;
|
||||
PBYTE pbData = nullptr;
|
||||
DWORD dwBytes = 0;
|
||||
|
||||
if (FetchImageFromUrl(offer.wszBannerUrl, &pbData, &dwBytes))
|
||||
{
|
||||
offer.pbBannerData = pbData;
|
||||
offer.dwBannerBytes = dwBytes;
|
||||
printf("[DLC] Banner fetched for '%ls' - %u bytes\n",
|
||||
offer.wszProductID, dwBytes);
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("[DLC] Banner fetch FAILED for '%ls' url='%ls'\n",
|
||||
offer.wszProductID, offer.wszBannerUrl);
|
||||
}
|
||||
}
|
||||
if (FetchImageFromUrl(offer.wszBannerUrl, &pbData, &dwBytes))
|
||||
{
|
||||
offer.pbBannerData = pbData;
|
||||
offer.dwBannerBytes = dwBytes;
|
||||
printf("[DLC] Banner fetched for '%ls' - %u bytes\n",
|
||||
offer.wszProductID, dwBytes);
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("[DLC] Banner fetch FAILED for '%ls' url='%ls'\n",
|
||||
offer.wszProductID, offer.wszBannerUrl);
|
||||
}
|
||||
}
|
||||
|
||||
// Register all offers with the app now that banners are populated.
|
||||
for (int i = 0; i < (int)m_offers.size(); i++)
|
||||
{
|
||||
const W64_OFFER_INFO& offer = m_offers[i];
|
||||
// Register all offers with the app now that banners are populated.
|
||||
for (int i = 0; i < (int)m_offers.size(); i++)
|
||||
{
|
||||
const W64_OFFER_INFO& offer = m_offers[i];
|
||||
|
||||
eDLCContentType eType = e_DLC_SkinPack;
|
||||
if (wcscmp(offer.wszType, L"MashUp") == 0) eType = e_DLC_MashupPacks;
|
||||
else if (wcscmp(offer.wszType, L"TexturePack") == 0) eType = e_DLC_TexturePacks;
|
||||
else if (wcscmp(offer.wszType, L"SkinPack") == 0) eType = e_DLC_SkinPack;
|
||||
eDLCContentType eType = e_DLC_SkinPack;
|
||||
if (wcscmp(offer.wszType, L"MashUp") == 0) eType = e_DLC_MashupPacks;
|
||||
else if (wcscmp(offer.wszType, L"TexturePack") == 0) eType = e_DLC_TexturePacks;
|
||||
else if (wcscmp(offer.wszType, L"SkinPack") == 0) eType = e_DLC_SkinPack;
|
||||
|
||||
printf("[DLC] Registering W64 DLC: '%ls' type=%d\n",
|
||||
offer.wszProductID, (int)eType);
|
||||
printf("[DLC] Registering W64 DLC: '%ls' type=%d\n",
|
||||
offer.wszProductID, (int)eType);
|
||||
|
||||
app.RegisterW64DLC(
|
||||
eType,
|
||||
offer.wszProductID, // key AND banner texture name
|
||||
offer.wszProductID, // banner texture name (used as lookup key in UpdateDisplay)
|
||||
0, // iConfig
|
||||
(unsigned int)i, // uiSortIndex
|
||||
offer.pbBannerData,
|
||||
offer.dwBannerBytes
|
||||
);
|
||||
}
|
||||
app.RegisterW64DLC(
|
||||
eType,
|
||||
offer.wszProductID, // key AND banner texture name
|
||||
offer.wszProductID, // banner texture name (used as lookup key in UpdateDisplay)
|
||||
0, // iConfig
|
||||
(unsigned int)i, // uiSortIndex
|
||||
offer.pbBannerData,
|
||||
offer.dwBannerBytes
|
||||
);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -67,7 +67,7 @@ public:
|
||||
|
||||
void InstallOffer(int iIndex,
|
||||
W64_INSTALL_CALLBACK pfnCallback = nullptr,
|
||||
void* pUserData = nullptr);
|
||||
void* pUserData = nullptr, wstring username = L"");
|
||||
|
||||
static bool FetchImageFromUrl(const wchar_t* url, PBYTE* ppData, DWORD* pdwSize);
|
||||
static bool FetchBytesFromUrl(const wchar_t* url, PBYTE* ppData, DWORD* pdwSize);
|
||||
@@ -95,6 +95,7 @@ private:
|
||||
int offerIndex;
|
||||
W64_INSTALL_CALLBACK pfnCallback;
|
||||
void* pUserData;
|
||||
wstring username;
|
||||
};
|
||||
|
||||
static DWORD WINAPI InstallThreadProc(LPVOID lpParam);
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
#include <Windows.h>
|
||||
|
||||
HWND GetMinecraftWindowHWND();
|
||||
@@ -0,0 +1,318 @@
|
||||
cmake_minimum_required(VERSION 3.14 FATAL_ERROR)
|
||||
|
||||
project (miniupnpc
|
||||
VERSION 2.3.3
|
||||
DESCRIPTION "UPnP IGD client lightweight library"
|
||||
HOMEPAGE_URL https://miniupnp.tuxfamily.org/
|
||||
LANGUAGES C)
|
||||
|
||||
set (MINIUPNPC_API_VERSION 21)
|
||||
|
||||
option (UPNPC_BUILD_STATIC "Build static library" TRUE)
|
||||
option (UPNPC_BUILD_SHARED "Build shared library" TRUE)
|
||||
option (UPNPC_BUILD_TESTS "Build test executables" TRUE)
|
||||
option (UPNPC_BUILD_SAMPLE "Build sample executables" TRUE)
|
||||
option (NO_GETADDRINFO "Define NO_GETADDRINFO" FALSE)
|
||||
option (UPNPC_NO_INSTALL "Disable installation" FALSE)
|
||||
|
||||
if (NOT UPNPC_BUILD_STATIC AND NOT UPNPC_BUILD_SHARED)
|
||||
message (FATAL "Both shared and static libraries are disabled!")
|
||||
endif ()
|
||||
|
||||
include(GNUInstallDirs)
|
||||
|
||||
# Interface library for compile definitions, flags and option
|
||||
add_library(miniupnpc-private INTERFACE)
|
||||
|
||||
if (NO_GETADDRINFO)
|
||||
target_compile_definitions(miniupnpc-private INTERFACE NO_GETADDRINFO)
|
||||
endif ()
|
||||
|
||||
if (NOT WIN32)
|
||||
target_compile_definitions(miniupnpc-private INTERFACE
|
||||
MINIUPNPC_SET_SOCKET_TIMEOUT
|
||||
MINIUPNPC_GET_SRC_ADDR
|
||||
_BSD_SOURCE _DEFAULT_SOURCE)
|
||||
if (NOT APPLE AND NOT CMAKE_SYSTEM_NAME MATCHES ".*BSD" AND NOT CMAKE_SYSTEM_NAME STREQUAL "SunOS")
|
||||
# add_definitions (-D_POSIX_C_SOURCE=200112L)
|
||||
target_compile_definitions(miniupnpc-private INTERFACE _XOPEN_SOURCE=600)
|
||||
endif ()
|
||||
if (CMAKE_SYSTEM_NAME STREQUAL "NetBSD")
|
||||
target_compile_definitions(miniupnpc-private INTERFACE _NETBSD_SOURCE)
|
||||
endif ()
|
||||
else ()
|
||||
set (MINIUPNPC_TARGET_WINDOWS_VERSION "0x0501" CACHE STRING "Minimum target Windows version as hex string") # XP or higher for getnameinfo and friends
|
||||
if (MINIUPNPC_TARGET_WINDOWS_VERSION)
|
||||
target_compile_definitions(miniupnpc-private INTERFACE _WIN32_WINNT=${MINIUPNPC_TARGET_WINDOWS_VERSION})
|
||||
endif ()
|
||||
endif ()
|
||||
|
||||
if (APPLE)
|
||||
target_compile_definitions(miniupnpc-private INTERFACE _DARWIN_C_SOURCE)
|
||||
endif ()
|
||||
|
||||
# Set compiler specific build flags
|
||||
if (CMAKE_COMPILER_IS_GNUCC AND NOT CMAKE_SYSTEM_NAME STREQUAL "AmigaOS")
|
||||
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
|
||||
target_compile_options(miniupnpc-private INTERFACE -Wall)
|
||||
endif ()
|
||||
|
||||
# Suppress noise warnings
|
||||
if (MSVC)
|
||||
target_compile_definitions(miniupnpc-private INTERFACE _CRT_SECURE_NO_WARNINGS _WINSOCK_DEPRECATED_NO_WARNINGS)
|
||||
endif()
|
||||
|
||||
configure_file (${CMAKE_CURRENT_SOURCE_DIR}/miniupnpcstrings.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/miniupnpcstrings.h)
|
||||
target_include_directories(miniupnpc-private INTERFACE $<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}>)
|
||||
|
||||
set (MINIUPNPC_SOURCES
|
||||
src/igd_desc_parse.c
|
||||
src/miniupnpc.c
|
||||
src/minixml.c
|
||||
src/minisoap.c
|
||||
src/minissdpc.c
|
||||
src/miniwget.c
|
||||
src/upnpcommands.c
|
||||
src/upnpdev.c
|
||||
src/upnpreplyparse.c
|
||||
src/upnperrors.c
|
||||
src/connecthostport.c
|
||||
src/portlistingparse.c
|
||||
src/receivedata.c
|
||||
src/addr_is_reserved.c
|
||||
${CMAKE_CURRENT_BINARY_DIR}/miniupnpcstrings.h
|
||||
)
|
||||
|
||||
if (WIN32)
|
||||
target_link_libraries(miniupnpc-private INTERFACE ws2_32 iphlpapi)
|
||||
elseif (CMAKE_SYSTEM_NAME STREQUAL "SunOS")
|
||||
target_link_libraries(miniupnpc-private INTERFACE socket nsl resolv)
|
||||
find_library (SOCKET_LIBRARY NAMES socket)
|
||||
find_library (NSL_LIBRARY NAMES nsl)
|
||||
find_library (RESOLV_LIBRARY NAMES resolv)
|
||||
set (LDLIBS ${SOCKET_LIBRARY} ${NSL_LIBRARY} ${RESOLV_LIBRARY} ${LDLIBS})
|
||||
elseif (HAIKU)
|
||||
target_link_libraries(miniupnpc-private INTERFACE network)
|
||||
find_library (SOCKET_LIBRARY NAMES network)
|
||||
find_library (NSL_LIBRARY NAMES network)
|
||||
find_library (RESOLV_LIBRARY NAMES network)
|
||||
set (LDLIBS ${SOCKET_LIBRARY} ${NSL_LIBRARY} ${RESOLV_LIBRARY} ${LDLIBS})
|
||||
endif ()
|
||||
|
||||
|
||||
if (UPNPC_BUILD_STATIC)
|
||||
add_library (libminiupnpc-static STATIC ${MINIUPNPC_SOURCES})
|
||||
target_include_directories (libminiupnpc-static PUBLIC
|
||||
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
|
||||
$<INSTALL_INTERFACE:include/miniupnpc>)
|
||||
if (NOT UPNPC_BUILD_SHARED)
|
||||
add_library (miniupnpc::miniupnpc ALIAS libminiupnpc-static)
|
||||
endif()
|
||||
set_target_properties (libminiupnpc-static PROPERTIES EXPORT_NAME miniupnpc)
|
||||
if (WIN32 AND NOT MINGW)
|
||||
set_target_properties (libminiupnpc-static PROPERTIES OUTPUT_NAME "libminiupnpc")
|
||||
else()
|
||||
set_target_properties (libminiupnpc-static PROPERTIES OUTPUT_NAME "miniupnpc")
|
||||
endif()
|
||||
target_link_libraries (libminiupnpc-static PRIVATE miniupnpc-private)
|
||||
target_include_directories(libminiupnpc-static INTERFACE $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>)
|
||||
target_compile_definitions(libminiupnpc-static PUBLIC MINIUPNP_STATICLIB)
|
||||
|
||||
if (NOT UPNPC_NO_INSTALL)
|
||||
install (TARGETS miniupnpc-private EXPORT miniupnpc-private)
|
||||
|
||||
install (EXPORT miniupnpc-private
|
||||
DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/miniupnpc"
|
||||
NAMESPACE miniupnpc::)
|
||||
|
||||
install (TARGETS libminiupnpc-static
|
||||
EXPORT libminiupnpc-static
|
||||
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
|
||||
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR})
|
||||
|
||||
install (EXPORT libminiupnpc-static
|
||||
DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/miniupnpc"
|
||||
NAMESPACE miniupnpc::)
|
||||
endif()
|
||||
|
||||
if (UPNPC_BUILD_SAMPLE)
|
||||
add_executable (upnpc-static src/upnpc.c)
|
||||
target_link_libraries (upnpc-static PRIVATE libminiupnpc-static)
|
||||
target_include_directories(upnpc-static PRIVATE ${CMAKE_CURRENT_BINARY_DIR})
|
||||
|
||||
add_executable (upnp-listdevices-static src/listdevices.c)
|
||||
target_link_libraries (upnp-listdevices-static PRIVATE libminiupnpc-static)
|
||||
target_include_directories(upnp-listdevices-static PRIVATE ${CMAKE_CURRENT_BINARY_DIR})
|
||||
|
||||
if (NOT UPNPC_NO_INSTALL)
|
||||
install (TARGETS upnpc-static upnp-listdevices-static
|
||||
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
|
||||
endif()
|
||||
endif ()
|
||||
endif ()
|
||||
|
||||
if (UPNPC_BUILD_SHARED)
|
||||
add_library (libminiupnpc-shared SHARED ${MINIUPNPC_SOURCES})
|
||||
target_include_directories (libminiupnpc-shared PUBLIC
|
||||
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
|
||||
$<INSTALL_INTERFACE:include/miniupnpc>)
|
||||
add_library (miniupnpc::miniupnpc ALIAS libminiupnpc-shared)
|
||||
set_target_properties (libminiupnpc-shared PROPERTIES EXPORT_NAME miniupnpc)
|
||||
set_target_properties (libminiupnpc-shared PROPERTIES OUTPUT_NAME "miniupnpc")
|
||||
set_target_properties (libminiupnpc-shared PROPERTIES VERSION ${PROJECT_VERSION})
|
||||
set_target_properties (libminiupnpc-shared PROPERTIES SOVERSION ${MINIUPNPC_API_VERSION})
|
||||
target_link_libraries (libminiupnpc-shared PRIVATE miniupnpc-private)
|
||||
target_compile_definitions(libminiupnpc-shared PRIVATE MINIUPNP_EXPORTS)
|
||||
|
||||
target_include_directories(libminiupnpc-shared INTERFACE $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>)
|
||||
if (WIN32)
|
||||
target_link_libraries(libminiupnpc-shared INTERFACE ws2_32 iphlpapi)
|
||||
endif()
|
||||
|
||||
if (NOT UPNPC_NO_INSTALL)
|
||||
install (TARGETS libminiupnpc-shared
|
||||
EXPORT libminiupnpc-shared
|
||||
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
|
||||
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR})
|
||||
|
||||
install (EXPORT libminiupnpc-shared
|
||||
DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/miniupnpc"
|
||||
NAMESPACE miniupnpc::)
|
||||
endif()
|
||||
|
||||
if (UPNPC_BUILD_SAMPLE)
|
||||
add_executable (upnpc-shared src/upnpc.c)
|
||||
target_link_libraries (upnpc-shared PRIVATE libminiupnpc-shared)
|
||||
target_include_directories(upnpc-shared PRIVATE ${CMAKE_CURRENT_BINARY_DIR})
|
||||
|
||||
add_executable (upnp-listdevices-shared src/listdevices.c)
|
||||
target_link_libraries (upnp-listdevices-shared PRIVATE libminiupnpc-shared)
|
||||
target_include_directories(upnp-listdevices-shared PRIVATE ${CMAKE_CURRENT_BINARY_DIR})
|
||||
|
||||
if (NOT UPNPC_NO_INSTALL)
|
||||
install (TARGETS upnpc-shared upnp-listdevices-shared
|
||||
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
|
||||
endif()
|
||||
endif ()
|
||||
endif ()
|
||||
|
||||
if (UPNPC_BUILD_TESTS)
|
||||
add_library(miniupnpc-tests INTERFACE)
|
||||
target_link_libraries(miniupnpc-tests INTERFACE miniupnpc-private)
|
||||
target_compile_definitions(miniupnpc-tests INTERFACE MINIUPNP_STATICLIB)
|
||||
|
||||
add_executable (testminixml src/testminixml.c src/minixml.c src/igd_desc_parse.c)
|
||||
target_include_directories (testminixml PRIVATE
|
||||
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>)
|
||||
target_link_libraries (testminixml PRIVATE miniupnpc-tests)
|
||||
|
||||
add_executable (minixmlvalid src/minixmlvalid.c src/minixml.c)
|
||||
target_link_libraries (minixmlvalid PRIVATE miniupnpc-tests)
|
||||
|
||||
add_executable (testupnpreplyparse src/testupnpreplyparse.c
|
||||
src/minixml.c src/upnpreplyparse.c)
|
||||
target_include_directories (testupnpreplyparse PRIVATE
|
||||
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>)
|
||||
target_link_libraries (testupnpreplyparse PRIVATE miniupnpc-tests)
|
||||
|
||||
add_executable (testigddescparse src/testigddescparse.c
|
||||
src/igd_desc_parse.c src/minixml.c src/miniupnpc.c src/miniwget.c src/minissdpc.c
|
||||
src/upnpcommands.c src/upnpreplyparse.c src/minisoap.c src/connecthostport.c
|
||||
src/portlistingparse.c src/receivedata.c src/addr_is_reserved.c
|
||||
)
|
||||
target_include_directories (testigddescparse PRIVATE
|
||||
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>)
|
||||
target_link_libraries (testigddescparse PRIVATE miniupnpc-tests)
|
||||
|
||||
add_executable (testminiwget src/testminiwget.c
|
||||
src/miniwget.c src/miniupnpc.c src/minisoap.c src/upnpcommands.c src/minissdpc.c
|
||||
src/upnpreplyparse.c src/minixml.c src/igd_desc_parse.c src/connecthostport.c
|
||||
src/portlistingparse.c src/receivedata.c src/addr_is_reserved.c
|
||||
)
|
||||
target_include_directories (testminiwget PRIVATE
|
||||
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>)
|
||||
target_link_libraries (testminiwget PRIVATE miniupnpc-tests)
|
||||
|
||||
add_executable (testaddr_is_reserved src/testaddr_is_reserved.c
|
||||
src/addr_is_reserved.c
|
||||
)
|
||||
target_link_libraries (testaddr_is_reserved PRIVATE miniupnpc-tests)
|
||||
|
||||
add_executable (testportlistingparse src/testportlistingparse.c
|
||||
src/minixml.c src/portlistingparse.c)
|
||||
target_include_directories (testportlistingparse PRIVATE
|
||||
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>)
|
||||
target_link_libraries (testportlistingparse PRIVATE miniupnpc-tests)
|
||||
|
||||
if (NOT WIN32)
|
||||
add_executable (minihttptestserver src/minihttptestserver.c)
|
||||
endif()
|
||||
|
||||
# set (UPNPC_INSTALL_TARGETS ${UPNPC_INSTALL_TARGETS} testminixml minixmlvalid testupnpreplyparse testigddescparse testminiwget)
|
||||
include(CTest)
|
||||
add_test(NAME validateminixml
|
||||
COMMAND minixmlvalid)
|
||||
add_test(NAME validateminiwget
|
||||
COMMAND ${CMAKE_SOURCE_DIR}/testminiwget.sh)
|
||||
if (NOT WIN32)
|
||||
set_property(TEST validateminiwget
|
||||
PROPERTY ENVIRONMENT
|
||||
TESTSERVER=${CMAKE_BINARY_DIR}/minihttptestserver
|
||||
TESTMINIWGET=${CMAKE_BINARY_DIR}/testminiwget)
|
||||
endif()
|
||||
add_test(NAME validateupnpreplyparse
|
||||
COMMAND ${CMAKE_SOURCE_DIR}/testupnpreplyparse.sh
|
||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR})
|
||||
set_property(TEST validateupnpreplyparse
|
||||
PROPERTY ENVIRONMENT
|
||||
TESTUPNPREPLYPARSE=${CMAKE_BINARY_DIR}/testupnpreplyparse)
|
||||
add_test(NAME validateportlistingparse
|
||||
COMMAND testportlistingparse)
|
||||
add_test(NAME validateigddescparse1
|
||||
COMMAND testigddescparse new_LiveBox_desc.xml new_LiveBox_desc.values
|
||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/testdesc)
|
||||
add_test(NAME validateigddescparse2
|
||||
COMMAND testigddescparse linksys_WAG200G_desc.xml linksys_WAG200G_desc.values
|
||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/testdesc)
|
||||
add_test(NAME validateaddr_is_reserved
|
||||
COMMAND testaddr_is_reserved)
|
||||
|
||||
endif ()
|
||||
|
||||
configure_file(miniupnpc.pc.in miniupnpc.pc @ONLY)
|
||||
|
||||
if (NOT UPNPC_NO_INSTALL)
|
||||
install (FILES
|
||||
include/miniupnpc.h
|
||||
include/miniwget.h
|
||||
include/upnpcommands.h
|
||||
include/igd_desc_parse.h
|
||||
include/upnpreplyparse.h
|
||||
include/upnperrors.h
|
||||
include/upnpdev.h
|
||||
include/miniupnpctypes.h
|
||||
include/portlistingparse.h
|
||||
include/miniupnpc_declspec.h
|
||||
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/miniupnpc
|
||||
)
|
||||
|
||||
install(FILES miniupnpc-config.cmake
|
||||
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/miniupnpc
|
||||
)
|
||||
|
||||
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/miniupnpc.pc
|
||||
DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig
|
||||
)
|
||||
|
||||
install(FILES man3/miniupnpc.3
|
||||
DESTINATION ${CMAKE_INSTALL_MANDIR}/man3
|
||||
)
|
||||
|
||||
install(FILES external-ip.sh
|
||||
TYPE BIN
|
||||
)
|
||||
endif()
|
||||
|
||||
# vim: ts=2:sw=2:expandtab
|
||||
@@ -0,0 +1,862 @@
|
||||
$Id: Changelog.txt,v 1.280 2025/05/25 21:56:47 nanard Exp $
|
||||
miniUPnP client Changelog.
|
||||
|
||||
VERSION 2.3.3 : released 2025/05/27
|
||||
|
||||
2025/05/25:
|
||||
define WIN32_LEAN_AND_MEAN
|
||||
|
||||
2025/03/29:
|
||||
fix a bug in ssdpDiscoverDevices() that has been there since 2010/04/05
|
||||
and the use of getaddrinfo()
|
||||
more argument checks in UPNP_AddAnyPortMapping() and
|
||||
UPNP_GetGenericPortMappingEntryExt()
|
||||
|
||||
2025/03/18:
|
||||
Add UPNP_GetSpecificPortMappingEntryExt() and
|
||||
UPNP_GetGenericPortMappingEntryExt() functions to support retrieving
|
||||
arbitrary long descriptions.
|
||||
Increments API_VERSION to 21
|
||||
|
||||
VERSION 2.3.2 : released 2025/03/05
|
||||
|
||||
2025/02/24:
|
||||
remove STRTOUI from public headers
|
||||
remove parseURL() from public headers
|
||||
remove struct NameValue from upnpreplyparse.h and use flexible array member
|
||||
Increments API_VERSION to 20
|
||||
|
||||
VERSION 2.3.1 : released 2025/02/23
|
||||
|
||||
2025/02/23:
|
||||
allocate argument arrays on stack instead of heap in upnpcommand.c
|
||||
|
||||
2025/02/09:
|
||||
doxygen documentation of the public headers
|
||||
|
||||
VERSION 2.3.0 : released 2025/01/11
|
||||
|
||||
2025/01/10:
|
||||
Change simpleUPnPcommand() prototype to remove unused first argument
|
||||
Increments API_VERSION to 19
|
||||
|
||||
2024/07/27:
|
||||
Add #define for UPNP_GetValidIGD() return values
|
||||
|
||||
VERSION 2.2.8 : released 2024/06/09
|
||||
|
||||
2024/05/16:
|
||||
IPv6: try site-local before link-local
|
||||
|
||||
2024/05/08:
|
||||
upnpc.c: Add -f option to upnpc program (delete multiple port redirections)
|
||||
UPNP_GetValidIGD(): distinguish between not connected and connected to a
|
||||
"private" network (with a reserved IP address).
|
||||
Increments API_VERSION to 18
|
||||
|
||||
VERSION 2.2.7 : released 2024/03/20
|
||||
|
||||
2024/01/15:
|
||||
listdevices.c: exit with status code 1 if no UPNP device is found
|
||||
|
||||
2024/01/07:
|
||||
upnpc.c: reformat Usage
|
||||
|
||||
VERSION 2.2.6 : released 2024/01/04
|
||||
|
||||
2024/01/04:
|
||||
includes charset="utf-8" in Content-Type
|
||||
fix memory allocation error in minissdpc.c
|
||||
|
||||
2023/06/15:
|
||||
Make User-Agent compliant.
|
||||
listdevices => upnp-listdevices
|
||||
|
||||
VERSION 2.2.5 : released 2023/06/12
|
||||
|
||||
2023/06/05:
|
||||
GetListOfPortMappings NewStartPort 0 => 1
|
||||
|
||||
2023/05/30:
|
||||
CheckPinholeWorking is optional
|
||||
add 60x errors from UPnP Device Architecture
|
||||
|
||||
2023/01/04:
|
||||
cmake: install binaries, man pages and external-ip.sh
|
||||
|
||||
VERSION 2.2.4 : released 2022/10/21
|
||||
|
||||
2022/02/20:
|
||||
upnpc: use of @ to replace local lan address
|
||||
|
||||
2021/11/09:
|
||||
python module : Allow to specify the root description url
|
||||
|
||||
VERSION 2.2.3 : released 2021/09/28
|
||||
|
||||
2021/08/13:
|
||||
Change directory structure : include/ and src/ directories.
|
||||
|
||||
VERSION 2.2.2 : released 2021/03/03
|
||||
|
||||
2021/01/15:
|
||||
miniupnpcmodule.c: throw an exception in UPnP_discover()
|
||||
|
||||
2020/12/30:
|
||||
Fix usage of IP_MULTICAST_IF with struct ip_mreqn
|
||||
|
||||
VERSION 2.2.1 : released 2020/12/20
|
||||
|
||||
2020/11/30:
|
||||
Add miniupnpc.rc for .dll description
|
||||
|
||||
VERSION 2.2.0 : released 2020/11/09
|
||||
|
||||
2020/09/24:
|
||||
Check properly for reserved IP addresses
|
||||
|
||||
2020/09/23:
|
||||
prevent infinite loop in upnpDiscover()
|
||||
|
||||
2020/02/16:
|
||||
Add Haiku support
|
||||
|
||||
2019/10/22:
|
||||
testminiwget.sh can use either "ip addr" or "ifconfig -a
|
||||
|
||||
2019/10/13:
|
||||
fix UPNP_GetValidIGD() when several devices are found
|
||||
which are reachable from != local address
|
||||
|
||||
2019/08/24:
|
||||
Allow Remote Host on upnpc command line
|
||||
fix error 708 description in strupnperror()
|
||||
|
||||
2019/04/05:
|
||||
Fix memory leak in upnpreplyparse.c with NewPortListing element
|
||||
|
||||
2019/03/10:
|
||||
connecthostport.c: Code simplification, error trace fix
|
||||
|
||||
2019/01/23:
|
||||
set timeout for select() in connecthostport()
|
||||
|
||||
2018/10/31:
|
||||
miniupnpcmodule.c: check return of WSAStartup()
|
||||
|
||||
2018/07/14:
|
||||
Fix and improve MSVC project :
|
||||
Add Dll configurations
|
||||
improve genminiupnpcstrings.vbs
|
||||
|
||||
2018/06/18:
|
||||
Fixes for windows 64-bits.
|
||||
|
||||
VERSION 2.1 : released 2018/05/07
|
||||
|
||||
2018/05/07:
|
||||
CMake Modernize and cleanup CMakeLists.txt
|
||||
Update MS Visual Studio projects
|
||||
|
||||
2018/04/30:
|
||||
listdevices: show devices sorted by XML desc URL
|
||||
|
||||
2018/04/26:
|
||||
Small fix in miniupnpcmodule.c (python module)
|
||||
Support cross compiling in Makefile.mingw
|
||||
|
||||
2018/04/06:
|
||||
Use SOCKET type instead of int (for Win64 compilation)
|
||||
Increments API_VERSION to 17
|
||||
|
||||
2018/02/22:
|
||||
Disable usage of MiniSSDPd when using -m option
|
||||
|
||||
2017/12/11:
|
||||
Fix buffer over run in minixml.c
|
||||
Fix uninitialized variable access in upnpreplyparse.c
|
||||
|
||||
2017/05/05:
|
||||
Fix CVE-2017-8798 Thanks to tin/Team OSTStrom
|
||||
|
||||
2016/11/11:
|
||||
check strlen before memcmp in XML parsing portlistingparse.c
|
||||
fix build under SOLARIS and CYGWIN
|
||||
|
||||
2016/10/11:
|
||||
Add python 3 compatibility to IGD test
|
||||
|
||||
VERSION 2.0 : released 2016/04/19
|
||||
|
||||
2016/01/24:
|
||||
change miniwget to return HTTP status code
|
||||
increments API_VERSION to 16
|
||||
|
||||
2016/01/22:
|
||||
Improve UPNPIGD_IsConnected() to check if WAN address is not private.
|
||||
parse HTTP response status line in miniwget.c
|
||||
|
||||
2015/10/26:
|
||||
snprintf() overflow check. check overflow in simpleUPnPcommand2()
|
||||
|
||||
2015/10/25:
|
||||
fix compilation with old macs
|
||||
fix compilation with mingw32 (for Appveyor)
|
||||
fix python module for python <= 2.3
|
||||
|
||||
2015/10/08:
|
||||
Change sameport to localport
|
||||
see https://github.com/miniupnp/miniupnp/pull/120
|
||||
increments API_VERSION to 15
|
||||
|
||||
2015/09/15:
|
||||
Fix buffer overflow in igd_desc_parse.c/IGDstartelt()
|
||||
Discovered by Aleksandar Nikolic of Cisco Talos
|
||||
|
||||
2015/08/28:
|
||||
move ssdpDiscoverDevices() to minissdpc.c
|
||||
|
||||
2015/08/27:
|
||||
avoid unix socket leak in getDevicesFromMiniSSDPD()
|
||||
|
||||
2015/08/16:
|
||||
Also accept "Up" as ConnectionStatus value
|
||||
|
||||
2015/07/23:
|
||||
split getDevicesFromMiniSSDPD
|
||||
add ttl argument to upnpDiscover() functions
|
||||
increments API_VERSION to 14
|
||||
|
||||
2015/07/22:
|
||||
Read USN from SSDP messages.
|
||||
|
||||
2015/07/15:
|
||||
Check malloc/calloc
|
||||
|
||||
2015/06/16:
|
||||
update getDevicesFromMiniSSDPD() to process longer minissdpd
|
||||
responses
|
||||
|
||||
2015/05/22:
|
||||
add searchalltypes param to upnpDiscoverDevices()
|
||||
increments API_VERSION to 13
|
||||
|
||||
2015/04/30:
|
||||
upnpc: output version on the terminal
|
||||
|
||||
2015/04/27:
|
||||
_BSD_SOURCE is deprecated in favor of _DEFAULT_SOURCE
|
||||
fix CMakeLists.txt COMPILE_DEFINITIONS
|
||||
fix getDevicesFromMiniSSDPD() not setting scope_id
|
||||
improve -r command of upnpc command line tool
|
||||
|
||||
2014/11/17:
|
||||
search all :
|
||||
upnpDiscoverDevices() / upnpDiscoverAll() functions
|
||||
listdevices executable
|
||||
increment API_VERSION to 12
|
||||
validate igd_desc_parse
|
||||
|
||||
2014/11/13:
|
||||
increment API_VERSION to 11
|
||||
|
||||
2014/11/05:
|
||||
simplified function GetUPNPUrls()
|
||||
|
||||
2014/09/11:
|
||||
use remoteHost arg of DeletePortMapping
|
||||
|
||||
2014/09/06:
|
||||
Fix python3 build
|
||||
|
||||
2014/07/01:
|
||||
Fix parsing of IGD2 root descriptions
|
||||
|
||||
2014/06/10:
|
||||
rename LIBSPEC to MINIUPNP_LIBSPEC
|
||||
|
||||
2014/05/15:
|
||||
Add support for IGD2 AddAnyPortMapping and DeletePortMappingRange
|
||||
|
||||
2014/02/05:
|
||||
handle EINPROGRESS after connect()
|
||||
|
||||
2014/02/03:
|
||||
minixml now handle XML comments
|
||||
|
||||
VERSION 1.9 : released 2014/01/31
|
||||
|
||||
2014/01/31:
|
||||
added argument remoteHost to UPNP_GetSpecificPortMappingEntry()
|
||||
increment API_VERSION to 10
|
||||
|
||||
2013/12/09:
|
||||
--help and -h arguments in upnpc.c
|
||||
|
||||
2013/10/07:
|
||||
fixed potential buffer overrun in miniwget.c
|
||||
Modified UPNP_GetValidIGD() to check for ExternalIpAddress
|
||||
|
||||
2013/08/01:
|
||||
define MAXHOSTNAMELEN if not already done
|
||||
|
||||
2013/06/06:
|
||||
update upnpreplyparse to allow larger values (128 chars instead of 64)
|
||||
|
||||
2013/05/14:
|
||||
Update upnpreplyparse to take into account "empty" elements
|
||||
validate upnpreplyparse.c code with "make check"
|
||||
|
||||
2013/05/03:
|
||||
Fix Solaris build thanks to Maciej Małecki
|
||||
|
||||
2013/04/27:
|
||||
Fix testminiwget.sh for BSD
|
||||
|
||||
2013/03/23:
|
||||
Fixed Makefile for *BSD
|
||||
|
||||
2013/03/11:
|
||||
Update Makefile to use JNAerator version 0.11
|
||||
|
||||
2013/02/11:
|
||||
Fix testminiwget.sh for use with dash
|
||||
Use $(DESTDIR) in Makefile
|
||||
|
||||
VERSION 1.8 : released 2013/02/06
|
||||
|
||||
2012/10/16:
|
||||
fix testminiwget with no IPv6 support
|
||||
|
||||
2012/09/27:
|
||||
Rename all include guards to not clash with C99
|
||||
(7.1.3 Reserved identifiers).
|
||||
|
||||
2012/08/30:
|
||||
Added -e option to upnpc program (set description for port mappings)
|
||||
|
||||
2012/08/29:
|
||||
Python 3 support (thanks to Christopher Foo)
|
||||
|
||||
2012/08/11:
|
||||
Fix a memory link in UPNP_GetValidIGD()
|
||||
Try to handle scope id in link local IPv6 URL under MS Windows
|
||||
|
||||
2012/07/20:
|
||||
Disable HAS_IP_MREQN on DragonFly BSD
|
||||
|
||||
2012/06/28:
|
||||
GetUPNPUrls() now inserts scope into link-local IPv6 addresses
|
||||
|
||||
2012/06/23:
|
||||
More error return checks in upnpc.c
|
||||
#define MINIUPNPC_GET_SRC_ADDR enables receivedata() to get scope_id
|
||||
parseURL() now parses IPv6 addresses scope
|
||||
new parameter for miniwget() : IPv6 address scope
|
||||
increment API_VERSION to 9
|
||||
|
||||
2012/06/20:
|
||||
fixed CMakeLists.txt
|
||||
|
||||
2012/05/29
|
||||
Improvements in testminiwget.sh
|
||||
|
||||
VERSION 1.7 : released 2012/05/24
|
||||
|
||||
2012/05/01:
|
||||
Cleanup settings of CFLAGS in Makefile
|
||||
Fix signed/unsigned integer comparaisons
|
||||
|
||||
2012/04/20:
|
||||
Allow to specify protocol with TCP or UDP for -A option
|
||||
|
||||
2012/04/09:
|
||||
Only try to fetch XML description once in UPNP_GetValidIGD()
|
||||
Added -ansi flag to compilation, and fixed C++ comments to ANSI C comments.
|
||||
|
||||
2012/04/05:
|
||||
minor improvements to minihttptestserver.c
|
||||
|
||||
2012/03/15:
|
||||
upnperrors.c returns valid error string for unrecognized error codes
|
||||
|
||||
2012/03/08:
|
||||
make minihttptestserver listen on loopback interface instead of 0.0.0.0
|
||||
|
||||
2012/01/25:
|
||||
Maven installation thanks to Alexey Kuznetsov
|
||||
|
||||
2012/01/21:
|
||||
Replace WIN32 macro by _WIN32
|
||||
|
||||
2012/01/19:
|
||||
Fixes in java wrappers thanks to Alexey Kuznetsov :
|
||||
https://github.com/axet/miniupnp/tree/fix-javatest/miniupnpc
|
||||
Make and install .deb packages (python) thanks to Alexey Kuznetsov :
|
||||
https://github.com/axet/miniupnp/tree/feature-debbuild/miniupnpc
|
||||
|
||||
2012/01/07:
|
||||
The multicast interface can now be specified by name with IPv4.
|
||||
|
||||
2012/01/02:
|
||||
Install man page
|
||||
|
||||
2011/11/25:
|
||||
added header to Port Mappings list in upnpc.c
|
||||
|
||||
2011/10/09:
|
||||
Makefile : make clean now removes jnaerator generated files.
|
||||
MINIUPNPC_VERSION in miniupnpc.h (updated by make)
|
||||
|
||||
2011/09/12:
|
||||
added rootdescURL to UPNPUrls structure.
|
||||
|
||||
VERSION 1.6 : released 2011/07/25
|
||||
|
||||
2011/07/25:
|
||||
Update doc for version 1.6 release
|
||||
|
||||
2011/06/18:
|
||||
Fix for windows in miniwget.c
|
||||
|
||||
2011/06/04:
|
||||
display remote host in port mapping listing
|
||||
|
||||
2011/06/03:
|
||||
Fix in make install : there were missing headers
|
||||
|
||||
2011/05/26:
|
||||
Fix the socket leak in miniwget thanks to Richard Marsh.
|
||||
Permit to add leaseduration in -a command. Display lease duration.
|
||||
|
||||
2011/05/15:
|
||||
Try both LinkLocal and SiteLocal multicast address for SSDP in IPv6
|
||||
|
||||
2011/05/09:
|
||||
add a test in testminiwget.sh.
|
||||
more error checking in miniwget.c
|
||||
|
||||
2011/05/06:
|
||||
Adding some tool to test and validate miniwget.c
|
||||
simplified and debugged miniwget.c
|
||||
|
||||
2011/04/11:
|
||||
moving ReceiveData() to a receivedata.c file.
|
||||
parsing presentation url
|
||||
adding IGD v2 WANIPv6FirewallControl commands
|
||||
|
||||
2011/04/10:
|
||||
update of miniupnpcmodule.c
|
||||
comments in miniwget.c, update in testminiwget
|
||||
Adding errors codes from IGD v2
|
||||
new functions in upnpc.c for IGD v2
|
||||
|
||||
2011/04/09:
|
||||
Support for litteral ip v6 address in miniwget
|
||||
|
||||
2011/04/08:
|
||||
Adding support for urn:schemas-upnp-org:service:WANIPv6FirewallControl:1
|
||||
Updating APIVERSION
|
||||
Supporting IPV6 in upnpDiscover()
|
||||
Adding a -6 option to upnpc command line tool
|
||||
|
||||
2011/03/18:
|
||||
miniwget/parseURL() : return an error when url param is null.
|
||||
fixing GetListOfPortMappings()
|
||||
|
||||
2011/03/14:
|
||||
upnpDiscover() now reporting an error code.
|
||||
improvements in comments.
|
||||
|
||||
2011/03/11:
|
||||
adding miniupnpcstrings.h.cmake and CMakeLists.txt files.
|
||||
|
||||
2011/02/15:
|
||||
Implementation of GetListOfPortMappings()
|
||||
|
||||
2011/02/07:
|
||||
updates to minixml to support character data starting with spaces
|
||||
minixml now support CDATA
|
||||
upnpreplyparse treats <NewPortListing> specificaly
|
||||
change in simpleUPnPcommand to return the buffer (simplification)
|
||||
|
||||
2011/02/06:
|
||||
Added leaseDuration argument to AddPortMapping()
|
||||
Starting to implement GetListOfPortMappings()
|
||||
|
||||
2011/01/11:
|
||||
updating wingenminiupnpcstrings.c
|
||||
|
||||
2011/01/04:
|
||||
improving updateminiupnpcstrings.sh
|
||||
|
||||
VERSION 1.5 : released 2011/01/01
|
||||
|
||||
2010/12/21:
|
||||
use NO_GETADDRINFO macro to disable the use of getaddrinfo/freeaddrinfo
|
||||
|
||||
2010/12/11:
|
||||
Improvements on getHTTPResponse() code.
|
||||
|
||||
2010/12/09:
|
||||
new code for miniwget that handle Chunked transfer encoding
|
||||
using getHTTPResponse() in SOAP call code
|
||||
Adding MANIFEST.in for 'python setup.py bdist_rpm'
|
||||
|
||||
2010/11/25:
|
||||
changes to minissdpc.c to compile under Win32.
|
||||
see http://miniupnp.tuxfamily.org/forum/viewtopic.php?t=729
|
||||
|
||||
2010/09/17:
|
||||
Various improvement to Makefile from Michał Górny
|
||||
|
||||
2010/08/05:
|
||||
Adding the script "external-ip.sh" from Reuben Hawkins
|
||||
|
||||
2010/06/09:
|
||||
update to python module to match modification made on 2010/04/05
|
||||
update to Java test code to match modification made on 2010/04/05
|
||||
all UPNP_* function now return an error if the SOAP request failed
|
||||
at HTTP level.
|
||||
|
||||
2010/04/17:
|
||||
Using GetBestRoute() under win32 in order to find the
|
||||
right interface to use.
|
||||
|
||||
2010/04/12:
|
||||
Retrying with HTTP/1.1 if HTTP/1.0 failed. see
|
||||
http://miniupnp.tuxfamily.org/forum/viewtopic.php?p=1703
|
||||
|
||||
2010/04/07:
|
||||
avoid returning duplicates in upnpDiscover()
|
||||
|
||||
2010/04/05:
|
||||
Create a connecthostport.h/.c with connecthostport() function
|
||||
and use it in miniwget and miniupnpc.
|
||||
Use getnameinfo() instead of inet_ntop or inet_ntoa
|
||||
Work to make miniupnpc IPV6 compatible...
|
||||
Add java test code.
|
||||
Big changes in order to support device having both WANIPConnection
|
||||
and WANPPPConnection.
|
||||
|
||||
2010/04/04:
|
||||
Use getaddrinfo() instead of gethostbyname() in miniwget.
|
||||
|
||||
2010/01/06:
|
||||
#define _DARWIN_C_SOURCE for Mac OS X
|
||||
|
||||
2009/12/19:
|
||||
Improve MinGW32 build
|
||||
|
||||
2009/12/11:
|
||||
adding a MSVC9 project to build the static library and executable
|
||||
|
||||
2009/12/10:
|
||||
Fixing some compilation stuff for Windows/MinGW
|
||||
|
||||
2009/12/07:
|
||||
adaptations in Makefile and updateminiupnpcstring.sh for AmigaOS
|
||||
some fixes for Windows when using virtual ethernet adapters (it is the
|
||||
case with VMWare installed).
|
||||
|
||||
2009/12/04:
|
||||
some fixes for AmigaOS compilation
|
||||
Changed HTTP version to HTTP/1.0 for Soap too (to prevent chunked
|
||||
transfer encoding)
|
||||
|
||||
2009/12/03:
|
||||
updating printIDG and testigddescparse.c for debug.
|
||||
modifications to compile under AmigaOS
|
||||
adding a testminiwget program
|
||||
Changed miniwget to advertise itself as HTTP/1.0 to prevent chunked
|
||||
transfer encoding
|
||||
|
||||
2009/11/26:
|
||||
fixing updateminiupnpcstrings.sh to take into account
|
||||
which command that does not return an error code.
|
||||
|
||||
VERSION 1.4 : released 2009/10/30
|
||||
|
||||
2009/10/16:
|
||||
using Py_BEGIN_ALLOW_THREADS and Py_END_ALLOW_THREADS in python module.
|
||||
|
||||
2009/10/10:
|
||||
Some fixes for compilation under Solaris
|
||||
compilation fixes : http://miniupnp.tuxfamily.org/forum/viewtopic.php?p=1464
|
||||
|
||||
2009/09/21:
|
||||
fixing the code to ignore EINTR during connect() calls.
|
||||
|
||||
2009/08/07:
|
||||
Set socket timeout for connect()
|
||||
Some cleanup in miniwget.c
|
||||
|
||||
2009/08/04:
|
||||
remove multiple redirections with -d in upnpc.c
|
||||
Print textual error code in upnpc.c
|
||||
Ignore EINTR during the connect() and poll() calls.
|
||||
|
||||
2009/07/29:
|
||||
fix in updateminiupnpcstrings.sh if OS name contains "/"
|
||||
Sending a correct value for MX: field in SSDP request
|
||||
|
||||
2009/07/20:
|
||||
Change the Makefile to compile under Mac OS X
|
||||
Fixed a stackoverflow in getDevicesFromMiniSSDPD()
|
||||
|
||||
2009/07/09:
|
||||
Compile under Haiku
|
||||
generate miniupnpcstrings.h.in from miniupnpcstrings.h
|
||||
|
||||
2009/06/04:
|
||||
patching to compile under CygWin and cross compile for minGW
|
||||
|
||||
VERSION 1.3 :
|
||||
|
||||
2009/04/17:
|
||||
updating python module
|
||||
Use strtoull() when using C99
|
||||
|
||||
2009/02/28:
|
||||
Fixed miniwget.c for compiling under sun
|
||||
|
||||
2008/12/18:
|
||||
cleanup in Makefile (thanks to Paul de Weerd)
|
||||
minissdpc.c : win32 compatibility
|
||||
miniupnpc.c : changed xmlns prefix from 'm' to 'u'
|
||||
Removed NDEBUG (using DEBUG)
|
||||
|
||||
2008/10/14:
|
||||
Added the ExternalHost argument to DeletePortMapping()
|
||||
|
||||
2008/10/11:
|
||||
Added the ExternalHost argument to AddPortMapping()
|
||||
Put a correct User-Agent: header in HTTP requests.
|
||||
|
||||
VERSION 1.2 :
|
||||
|
||||
2008/10/07:
|
||||
Update docs
|
||||
|
||||
2008/09/25:
|
||||
Integrated sameport patch from Dario Meloni : Added a "sameport"
|
||||
argument to upnpDiscover().
|
||||
|
||||
2008/07/18:
|
||||
small modif to make Clang happy :)
|
||||
|
||||
2008/07/17:
|
||||
#define SOAPPREFIX "s" in miniupnpc.c in order to remove SOAP-ENV...
|
||||
|
||||
2008/07/14:
|
||||
include declspec.h in installation (to /usr/include/miniupnpc)
|
||||
|
||||
VERSION 1.1 :
|
||||
|
||||
2008/07/04:
|
||||
standard options for install/ln instead of gnu-specific stuff.
|
||||
|
||||
2008/07/03:
|
||||
now builds a .dll and .lib with win32. (mingw32)
|
||||
|
||||
2008/04/28:
|
||||
make install now install the binary of the upnpc tool
|
||||
|
||||
2008/04/27:
|
||||
added testupnpigd.py
|
||||
added error strings for miniupnpc "internal" errors
|
||||
improved python module error/exception reporting.
|
||||
|
||||
2008/04/23:
|
||||
Completely rewrite igd_desc_parse.c in order to be compatible with
|
||||
Linksys WAG200G
|
||||
Added testigddescparse
|
||||
updated python module
|
||||
|
||||
VERSION 1.0 :
|
||||
|
||||
2008/02/21:
|
||||
put some #ifdef DEBUG around DisplayNameValueList()
|
||||
|
||||
2008/02/18:
|
||||
Improved error reporting in upnpcommands.c
|
||||
UPNP_GetStatusInfo() returns LastConnectionError
|
||||
|
||||
2008/02/16:
|
||||
better error handling in minisoap.c
|
||||
improving display of "valid IGD found" in upnpc.c
|
||||
|
||||
2008/02/03:
|
||||
Fixing UPNP_GetValidIGD()
|
||||
improved make install :)
|
||||
|
||||
2007/12/22:
|
||||
Adding upnperrors.c/h to provide a strupnperror() function
|
||||
used to translate UPnP error codes to string.
|
||||
|
||||
2007/12/19:
|
||||
Fixing getDevicesFromMiniSSDPD()
|
||||
improved error reporting of UPnP functions
|
||||
|
||||
2007/12/18:
|
||||
It is now possible to specify a different location for MiniSSDPd socket.
|
||||
working with MiniSSDPd is now more efficient.
|
||||
python module improved.
|
||||
|
||||
2007/12/16:
|
||||
improving error reporting
|
||||
|
||||
2007/12/13:
|
||||
Try to improve compatibility by using HTTP/1.0 instead of 1.1 and
|
||||
XML a bit different for SOAP.
|
||||
|
||||
2007/11/25:
|
||||
fixed select() call for linux
|
||||
|
||||
2007/11/15:
|
||||
Added -fPIC to CFLAG for better shared library code.
|
||||
|
||||
2007/11/02:
|
||||
Fixed a potential socket leak in miniwget2()
|
||||
|
||||
2007/10/16:
|
||||
added a parameter to upnpDiscover() in order to allow the use of another
|
||||
interface than the default multicast interface.
|
||||
|
||||
2007/10/12:
|
||||
Fixed the creation of symbolic link in Makefile
|
||||
|
||||
2007/10/08:
|
||||
Added man page
|
||||
|
||||
2007/10/02:
|
||||
fixed memory bug in GetUPNPUrls()
|
||||
|
||||
2007/10/01:
|
||||
fixes in the Makefile
|
||||
Added UPNP_GetIGDFromUrl() and adapted the sample program accordingly.
|
||||
Added SONAME in the shared library to please debian :)
|
||||
fixed MS Windows compilation (minissdpd is not available under MS Windows).
|
||||
|
||||
2007/09/25:
|
||||
small change to Makefile to be able to install in a different location
|
||||
(default is /usr)
|
||||
|
||||
2007/09/24:
|
||||
now compiling both shared and static library
|
||||
|
||||
2007/09/19:
|
||||
Cosmetic changes on upnpc.c
|
||||
|
||||
2007/09/02:
|
||||
adapting to new miniSSDPd (release version ?)
|
||||
|
||||
2007/08/31:
|
||||
Usage of miniSSDPd to skip discovery process.
|
||||
|
||||
2007/08/27:
|
||||
fixed python module to allow compilation with Python older than Python 2.4
|
||||
|
||||
2007/06/12:
|
||||
Added a python module.
|
||||
|
||||
2007/05/19:
|
||||
Fixed compilation under MinGW
|
||||
|
||||
2007/05/15:
|
||||
fixed a memory leak in AddPortMapping()
|
||||
Added testupnpreplyparse executable to check the parsing of
|
||||
upnp soap messages
|
||||
minixml now ignore namespace prefixes.
|
||||
|
||||
2007/04/26:
|
||||
upnpc now displays external ip address with -s or -l
|
||||
|
||||
2007/04/11:
|
||||
changed MINIUPNPC_URL_MAXSIZE to 128 to accommodate the "BT Voyager 210"
|
||||
|
||||
2007/03/19:
|
||||
cleanup in miniwget.c
|
||||
|
||||
2007/03/01:
|
||||
Small typo fix...
|
||||
|
||||
2007/01/30:
|
||||
Now parsing the HTTP header from SOAP responses in order to
|
||||
get content-length value.
|
||||
|
||||
2007/01/29:
|
||||
Fixed the Soap Query to speedup the HTTP request.
|
||||
added some Win32 DLL stuff...
|
||||
|
||||
2007/01/27:
|
||||
Fixed some WIN32 compatibility issues
|
||||
|
||||
2006/12/14:
|
||||
Added UPNPIGD_IsConnected() function in miniupnp.c/.h
|
||||
Added UPNP_GetValidIGD() in miniupnp.c/.h
|
||||
cleaned upnpc.c main(). now using UPNP_GetValidIGD()
|
||||
|
||||
2006/12/07:
|
||||
Version 1.0-RC1 released
|
||||
|
||||
2006/12/03:
|
||||
Minor changes to compile under SunOS/Solaris
|
||||
|
||||
2006/11/30:
|
||||
made a minixml parser validator program
|
||||
updated minixml to handle attributes correctly
|
||||
|
||||
2006/11/22:
|
||||
Added a -r option to the upnpc sample thanks to Alexander Hubmann.
|
||||
|
||||
2006/11/19:
|
||||
Cleanup code to make it more ANSI C compliant
|
||||
|
||||
2006/11/10:
|
||||
detect and display local lan address.
|
||||
|
||||
2006/11/04:
|
||||
Packets and Bytes Sent/Received are now unsigned int.
|
||||
|
||||
2006/11/01:
|
||||
Bug fix thanks to Giuseppe D'Angelo
|
||||
|
||||
2006/10/31:
|
||||
C++ compatibility for .h files.
|
||||
Added a way to get ip Address on the LAN used to reach the IGD.
|
||||
|
||||
2006/10/25:
|
||||
Added M-SEARCH to the services in the discovery process.
|
||||
|
||||
2006/10/22:
|
||||
updated the Makefile to use makedepend, added a "make install"
|
||||
update Makefile
|
||||
|
||||
2006/10/20:
|
||||
fixing the description url parsing thanks to patch sent by
|
||||
Wayne Dawe.
|
||||
Fixed/translated some comments.
|
||||
Implemented a better discover process, first looking
|
||||
for IGD then for root devices (as some devices only reply to
|
||||
M-SEARCH for root devices).
|
||||
|
||||
2006/09/02:
|
||||
added freeUPNPDevlist() function.
|
||||
|
||||
2006/08/04:
|
||||
More command line arguments checking
|
||||
|
||||
2006/08/01:
|
||||
Added the .bat file to compile under Win32 with minGW32
|
||||
|
||||
2006/07/31:
|
||||
Fixed the rootdesc parser (igd_desc_parse.c)
|
||||
|
||||
2006/07/20:
|
||||
parseMSEARCHReply() is now returning the ST: line as well
|
||||
starting changes to detect several UPnP devices on the network
|
||||
|
||||
2006/07/19:
|
||||
using GetCommonLinkProperties to get down/upload bitrate
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
MiniUPnPc is a library enabling applications to access the services provided
|
||||
by an UPnP "Internet Gateway Device" present on the network. In UPnP
|
||||
terminology, MiniUPnPc is a UPnP Control Point.
|
||||
|
||||
MiniUPnPc is designed to have low footprint and the libc as unique
|
||||
dependency.
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,29 @@
|
||||
BSD 3-Clause License
|
||||
|
||||
Copyright (c) 2005-2025, Thomas BERNARD
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
2. 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.
|
||||
|
||||
3. Neither the name of the copyright holder 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 THE COPYRIGHT HOLDER OR CONTRIBUTORS 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.
|
||||
@@ -0,0 +1,11 @@
|
||||
include README
|
||||
include VERSION
|
||||
include LICENSE
|
||||
include DESCRIPTION
|
||||
include src/miniupnpcmodule.c
|
||||
include setup.py
|
||||
include Makefile
|
||||
include src/*.[ch]
|
||||
include include/*.h
|
||||
include *.h.in
|
||||
include *.sh
|
||||
@@ -0,0 +1,411 @@
|
||||
# $Id: Makefile,v 1.156 2025/03/18 23:40:14 nanard Exp $
|
||||
# MiniUPnP Project
|
||||
# http://miniupnp.free.fr/
|
||||
# https://miniupnp.tuxfamily.org/
|
||||
# https://github.com/miniupnp/miniupnp
|
||||
# (c) 2005-2025 Thomas Bernard
|
||||
# to install use :
|
||||
# $ make DESTDIR=/tmp/dummylocation install
|
||||
# or
|
||||
# $ INSTALLPREFIX=/usr/local make install
|
||||
# or
|
||||
# $ make install (default INSTALLPREFIX is /usr)
|
||||
OS = $(shell $(CC) -dumpmachine)
|
||||
VERSION = $(shell cat VERSION)
|
||||
|
||||
ifneq (, $(findstring darwin, $(OS)))
|
||||
JARSUFFIX=mac
|
||||
LIBTOOL ?= $(shell which libtool)
|
||||
endif
|
||||
ifneq (, $(findstring linux, $(OS)))
|
||||
JARSUFFIX=linux
|
||||
endif
|
||||
ifneq (, $(findstring mingw, $(OS))$(findstring cygwin, $(OS))$(findstring msys, $(OS)))
|
||||
JARSUFFIX=win32
|
||||
endif
|
||||
|
||||
HAVE_IPV6 ?= yes
|
||||
export HAVE_IPV6
|
||||
|
||||
# directories
|
||||
INCDIR = include
|
||||
SRCDIR = src
|
||||
BUILD = build
|
||||
|
||||
CC ?= gcc
|
||||
#AR = gar
|
||||
#CFLAGS = -O -g
|
||||
# to debug :
|
||||
ASANFLAGS = -fsanitize=address -fsanitize=undefined -fsanitize=leak
|
||||
#CFLAGS = -g -ggdb -O0 $(ASANFLAGS) -fno-omit-frame-pointer
|
||||
#CPPFLAGS += -DDEBUG
|
||||
#LDFLAGS += $(ASANFLAGS)
|
||||
CFLAGS ?= -O
|
||||
CFLAGS += -Wall
|
||||
CFLAGS += -W -Wstrict-prototypes
|
||||
CFLAGS += -fno-common
|
||||
CPPFLAGS += -I$(BUILD)
|
||||
CPPFLAGS += -DMINIUPNPC_SET_SOCKET_TIMEOUT
|
||||
CPPFLAGS += -DMINIUPNPC_GET_SRC_ADDR
|
||||
CPPFLAGS += -D_BSD_SOURCE
|
||||
CPPFLAGS += -D_DEFAULT_SOURCE
|
||||
ifneq (, $(findstring netbsd, $(OS)))
|
||||
CPPFLAGS += -D_NETBSD_SOURCE
|
||||
endif
|
||||
ifeq (, $(findstring freebsd, $(OS))$(findstring darwin, $(OS)))
|
||||
#CPPFLAGS += -D_POSIX_C_SOURCE=200112L
|
||||
CPPFLAGS += -D_XOPEN_SOURCE=600
|
||||
endif
|
||||
#CFLAGS += -ansi
|
||||
#CPPFLAGS += -DNO_GETADDRINFO
|
||||
|
||||
DEPFLAGS = -MM -MG
|
||||
|
||||
MKDIR = mkdir -p
|
||||
INSTALL = install
|
||||
SH = /bin/sh
|
||||
JAVA = java
|
||||
# see http://code.google.com/p/jnaerator/
|
||||
#JNAERATOR = jnaerator-0.9.7.jar
|
||||
#JNAERATOR = jnaerator-0.9.8-shaded.jar
|
||||
#JNAERATORARGS = -library miniupnpc
|
||||
#JNAERATOR = jnaerator-0.10-shaded.jar
|
||||
#JNAERATOR = jnaerator-0.11-shaded.jar
|
||||
# https://repo1.maven.org/maven2/com/nativelibs4java/jnaerator/0.12/jnaerator-0.12-shaded.jar
|
||||
JNAERATOR = jnaerator-0.12-shaded.jar
|
||||
JNAERATORARGS = -mode StandaloneJar -runtime JNAerator -library miniupnpc
|
||||
#JNAERATORBASEURL = http://jnaerator.googlecode.com/files/
|
||||
JNAERATORBASEURL = https://repo1.maven.org/maven2/com/nativelibs4java/jnaerator/0.12
|
||||
|
||||
ifneq (, $(findstring sun, $(OS))$(findstring solaris, $(OS)))
|
||||
LDLIBS=-lsocket -lnsl -lresolv
|
||||
CPPFLAGS += -D__EXTENSIONS__
|
||||
CFLAGS += -std=c99
|
||||
endif
|
||||
|
||||
# APIVERSION is used to build SONAME
|
||||
APIVERSION = 21
|
||||
|
||||
SRCS = $(wildcard $(SRCDIR)/*.c)
|
||||
PRIVATE_HEADERS = $(wildcard $(SRCDIR)/*.h)
|
||||
|
||||
LIBOBJS = $(addprefix $(BUILD)/,miniwget.o minixml.o igd_desc_parse.o minisoap.o \
|
||||
miniupnpc.o upnpreplyparse.o upnpcommands.o upnperrors.o \
|
||||
connecthostport.o portlistingparse.o receivedata.o upnpdev.o \
|
||||
addr_is_reserved.o)
|
||||
|
||||
BUILDINCLUDES = $(addprefix $(BUILD)/, miniupnpcstrings.h)
|
||||
|
||||
OBJS = $(patsubst $(SRCDIR)/%.c,$(BUILD)/%.o,$(SRCS))
|
||||
DEPS = $(patsubst $(SRCDIR)/%.c,$(BUILD)/%.d,$(SRCS))
|
||||
|
||||
# HEADERS to install
|
||||
CPPFLAGS += -I$(INCDIR)
|
||||
HEADERS = $(wildcard $(INCDIR)/*.h)
|
||||
|
||||
# library names
|
||||
LIBRARY = $(BUILD)/libminiupnpc.a
|
||||
ifneq (, $(findstring darwin, $(OS)))
|
||||
SHAREDLIBRARY = $(BUILD)/libminiupnpc.dylib
|
||||
SONAME = $(notdir $(basename $(SHAREDLIBRARY))).$(APIVERSION).dylib
|
||||
CPPFLAGS += -D_DARWIN_C_SOURCE
|
||||
else
|
||||
ifeq ($(JARSUFFIX), win32)
|
||||
SHAREDLIBRARY = $(BUILD)/miniupnpc.dll
|
||||
else
|
||||
# Linux/BSD/etc.
|
||||
SHAREDLIBRARY = $(BUILD)/libminiupnpc.so
|
||||
SONAME = $(notdir $(SHAREDLIBRARY)).$(APIVERSION)
|
||||
endif
|
||||
endif
|
||||
|
||||
EXECUTABLES = $(addprefix $(BUILD)/, upnpc-static upnp-listdevices-static)
|
||||
EXECUTABLES_ADDTESTS = $(addprefix $(BUILD)/, testminixml minixmlvalid \
|
||||
testupnpreplyparse testigddescparse testminiwget testportlistingparse)
|
||||
|
||||
TESTMINIXMLOBJS = $(addprefix $(BUILD)/, minixml.o igd_desc_parse.o testminixml.o)
|
||||
|
||||
TESTMINIWGETOBJS = $(addprefix $(BUILD)/, miniwget.o testminiwget.o connecthostport.o receivedata.o)
|
||||
|
||||
TESTUPNPREPLYPARSE = $(addprefix $(BUILD)/, testupnpreplyparse.o minixml.o upnpreplyparse.o)
|
||||
|
||||
TESTPORTLISTINGPARSE = $(addprefix $(BUILD)/, testportlistingparse.o minixml.o portlistingparse.o)
|
||||
|
||||
TESTADDR_IS_RESERVED = $(addprefix $(BUILD)/, testaddr_is_reserved.o addr_is_reserved.o)
|
||||
|
||||
TESTIGDDESCPARSE = $(addprefix $(BUILD)/, testigddescparse.o igd_desc_parse.o minixml.o \
|
||||
miniupnpc.o miniwget.o upnpcommands.o upnpreplyparse.o \
|
||||
minisoap.o connecthostport.o receivedata.o \
|
||||
portlistingparse.o addr_is_reserved.o)
|
||||
|
||||
ifeq (, $(findstring amiga, $(OS)))
|
||||
ifeq (, $(findstring mingw, $(OS))$(findstring cygwin, $(OS))$(findstring msys, $(OS)))
|
||||
CFLAGS += -fPIC
|
||||
endif
|
||||
EXECUTABLES += $(BUILD)/upnpc-shared $(BUILD)/upnp-listdevices-shared
|
||||
TESTMINIWGETOBJS += $(BUILD)/minissdpc.o
|
||||
TESTIGDDESCPARSE += $(BUILD)/minissdpc.o
|
||||
LIBOBJS += $(BUILD)/minissdpc.o
|
||||
endif
|
||||
|
||||
LIBDIR ?= lib
|
||||
# install directories
|
||||
ifeq ($(strip $(PREFIX)),)
|
||||
INSTALLPREFIX ?= /usr
|
||||
else
|
||||
INSTALLPREFIX ?= $(PREFIX)
|
||||
endif
|
||||
INSTALLDIRINC = $(INSTALLPREFIX)/include/miniupnpc
|
||||
INSTALLDIRLIB = $(INSTALLPREFIX)/$(LIBDIR)
|
||||
INSTALLDIRBIN = $(INSTALLPREFIX)/bin
|
||||
INSTALLDIRMAN = $(INSTALLPREFIX)/share/man
|
||||
PKGCONFIGDIR = $(INSTALLDIRLIB)/pkgconfig
|
||||
|
||||
FILESTOINSTALL = $(LIBRARY) $(EXECUTABLES)
|
||||
ifeq (, $(findstring amiga, $(OS)))
|
||||
FILESTOINSTALL += $(SHAREDLIBRARY) $(BUILD)/miniupnpc.pc
|
||||
endif
|
||||
|
||||
|
||||
.PHONY: install clean depend all check test everything \
|
||||
installpythonmodule updateversion doc
|
||||
|
||||
all: $(LIBRARY) $(EXECUTABLES)
|
||||
|
||||
test: check
|
||||
|
||||
check: validateminixml validateminiwget validateupnpreplyparse \
|
||||
validateportlistingparse validateigddescparse validateaddr_is_reserved
|
||||
|
||||
everything: all $(EXECUTABLES_ADDTESTS)
|
||||
|
||||
doc: doxygen/html/index.html
|
||||
|
||||
# note : the python module "wheel" is required for bdist_wheel
|
||||
pythonmodule: $(LIBRARY) $(SRCDIR)/miniupnpcmodule.c setup.py
|
||||
MAKE=$(MAKE) python setup.py build
|
||||
MAKE=$(MAKE) python setup.py sdist
|
||||
MAKE=$(MAKE) python setup.py bdist_wheel --skip-build
|
||||
touch $@
|
||||
|
||||
installpythonmodule: pythonmodule
|
||||
MAKE=$(MAKE) python setup.py install
|
||||
|
||||
pythonmodule3: $(LIBRARY) $(SRCDIR)/miniupnpcmodule.c setup.py
|
||||
MAKE=$(MAKE) python3 setup.py build
|
||||
touch $@
|
||||
|
||||
installpythonmodule3: pythonmodule3
|
||||
MAKE=$(MAKE) python3 setup.py install
|
||||
|
||||
validateminixml: $(BUILD)/minixmlvalid
|
||||
@echo "minixml validation test"
|
||||
./$<
|
||||
touch $@
|
||||
|
||||
validateminiwget: testminiwget.sh $(BUILD)/testminiwget $(BUILD)/minihttptestserver
|
||||
@echo "miniwget validation test"
|
||||
./$<
|
||||
touch $@
|
||||
|
||||
validateupnpreplyparse: testupnpreplyparse.sh $(BUILD)/testupnpreplyparse
|
||||
@echo "upnpreplyparse validation test"
|
||||
./$<
|
||||
touch $@
|
||||
|
||||
validateportlistingparse: $(BUILD)/testportlistingparse
|
||||
@echo "portlistingparse validation test"
|
||||
./$<
|
||||
touch $@
|
||||
|
||||
validateigddescparse: $(BUILD)/testigddescparse
|
||||
@echo "igd desc parse validation test"
|
||||
./$< testdesc/new_LiveBox_desc.xml testdesc/new_LiveBox_desc.values
|
||||
./$< testdesc/linksys_WAG200G_desc.xml testdesc/linksys_WAG200G_desc.values
|
||||
touch $@
|
||||
|
||||
validateaddr_is_reserved: $(BUILD)/testaddr_is_reserved
|
||||
@echo "addr_is_reserved() validation test"
|
||||
./$<
|
||||
touch $@
|
||||
|
||||
clean:
|
||||
$(RM) $(LIBRARY) $(SHAREDLIBRARY) $(EXECUTABLES) $(OBJS) $(BUILDINCLUDES)
|
||||
$(RM) $(EXECUTABLES_ADDTESTS)
|
||||
# clean python stuff
|
||||
$(RM) pythonmodule pythonmodule3
|
||||
$(RM) validateminixml validateminiwget validateupnpreplyparse
|
||||
$(RM) validateigddescparse
|
||||
$(RM) minihttptestserver
|
||||
$(RM) testaddr_is_reserved
|
||||
$(RM) -r build/ dist/
|
||||
#python setup.py clean
|
||||
# clean jnaerator stuff
|
||||
$(RM) _jnaerator.* java/miniupnpc_$(OS).jar
|
||||
|
||||
distclean: clean
|
||||
$(RM) $(JNAERATOR) java/*.jar java/*.class out.errors.txt
|
||||
|
||||
updateversion: include/miniupnpc.h
|
||||
cp $< $<.bak
|
||||
sed 's/\(.*MINIUPNPC_API_VERSION\s\+\)[0-9]\+/\1$(APIVERSION)/' < $<.bak > $<
|
||||
|
||||
install: updateversion $(FILESTOINSTALL)
|
||||
$(INSTALL) -d $(DESTDIR)$(INSTALLDIRINC)
|
||||
$(INSTALL) -m 644 $(HEADERS) $(DESTDIR)$(INSTALLDIRINC)
|
||||
$(INSTALL) -d $(DESTDIR)$(INSTALLDIRLIB)
|
||||
$(INSTALL) -m 644 $(LIBRARY) $(DESTDIR)$(INSTALLDIRLIB)
|
||||
ifeq (, $(findstring amiga, $(OS)))
|
||||
$(INSTALL) -m 644 $(SHAREDLIBRARY) $(DESTDIR)$(INSTALLDIRLIB)/$(SONAME)
|
||||
ln -fs $(SONAME) $(DESTDIR)$(INSTALLDIRLIB)/$(notdir $(SHAREDLIBRARY))
|
||||
$(INSTALL) -d $(DESTDIR)$(PKGCONFIGDIR)
|
||||
$(INSTALL) -m 644 $(BUILD)/miniupnpc.pc $(DESTDIR)$(PKGCONFIGDIR)
|
||||
endif
|
||||
$(INSTALL) -d $(DESTDIR)$(INSTALLDIRBIN)
|
||||
ifneq (, $(findstring amiga, $(OS)))
|
||||
$(INSTALL) -m 755 $(BUILD)/upnpc-static $(DESTDIR)$(INSTALLDIRBIN)/upnpc
|
||||
$(INSTALL) -m 755 $(BUILD)/upnp-listdevices-static $(DESTDIR)$(INSTALLDIRBIN)/upnp-listdevices
|
||||
else
|
||||
$(INSTALL) -m 755 $(BUILD)/upnpc-shared $(DESTDIR)$(INSTALLDIRBIN)/upnpc
|
||||
$(INSTALL) -m 755 $(BUILD)/upnp-listdevices-shared $(DESTDIR)$(INSTALLDIRBIN)/upnp-listdevices
|
||||
endif
|
||||
$(INSTALL) -m 755 external-ip.sh $(DESTDIR)$(INSTALLDIRBIN)/external-ip
|
||||
ifeq (, $(findstring amiga, $(OS)))
|
||||
$(INSTALL) -d $(DESTDIR)$(INSTALLDIRMAN)/man3
|
||||
$(INSTALL) -m 644 man3/miniupnpc.3 $(DESTDIR)$(INSTALLDIRMAN)/man3/miniupnpc.3
|
||||
ifneq (, $(findstring linux, $(OS)))
|
||||
gzip -f $(DESTDIR)$(INSTALLDIRMAN)/man3/miniupnpc.3
|
||||
endif
|
||||
endif
|
||||
|
||||
install-static: updateversion $(FILESTOINSTALL)
|
||||
$(INSTALL) -d $(DESTDIR)$(INSTALLDIRINC)
|
||||
$(INSTALL) -m 644 $(HEADERS) $(DESTDIR)$(INSTALLDIRINC)
|
||||
$(INSTALL) -d $(DESTDIR)$(INSTALLDIRLIB)
|
||||
$(INSTALL) -m 644 $(LIBRARY) $(DESTDIR)$(INSTALLDIRLIB)
|
||||
$(INSTALL) -d $(DESTDIR)$(INSTALLDIRBIN)
|
||||
$(INSTALL) -m 755 external-ip.sh $(DESTDIR)$(INSTALLDIRBIN)/external-ip
|
||||
|
||||
cleaninstall:
|
||||
$(RM) -r $(DESTDIR)$(INSTALLDIRINC)
|
||||
$(RM) $(DESTDIR)$(INSTALLDIRLIB)/$(LIBRARY)
|
||||
$(RM) $(DESTDIR)$(INSTALLDIRLIB)/$(SHAREDLIBRARY)
|
||||
|
||||
$(BUILD)/miniupnpc.pc: VERSION
|
||||
@$(MKDIR) $(@D)
|
||||
$(RM) $@
|
||||
echo "prefix=$(INSTALLPREFIX)" >> $@
|
||||
echo "exec_prefix=\$${prefix}" >> $@
|
||||
echo "libdir=\$${exec_prefix}/$(LIBDIR)" >> $@
|
||||
echo "includedir=\$${prefix}/include" >> $@
|
||||
echo "" >> $@
|
||||
echo "Name: miniUPnPc" >> $@
|
||||
echo "Description: UPnP IGD client lightweight library" >> $@
|
||||
echo "URL: https://miniupnp.tuxfamily.org/" >> $@
|
||||
echo "Version: $(VERSION)" >> $@
|
||||
echo "Libs: -L\$${libdir} -lminiupnpc" >> $@
|
||||
echo "Cflags: -I\$${includedir}" >> $@
|
||||
|
||||
depend: $(DEPS)
|
||||
|
||||
$(LIBRARY): $(LIBOBJS)
|
||||
ifneq (, $(findstring darwin, $(OS)))
|
||||
$(LIBTOOL) -static -o $@ $?
|
||||
else
|
||||
$(AR) crs $@ $?
|
||||
endif
|
||||
|
||||
$(SHAREDLIBRARY): $(LIBOBJS)
|
||||
ifneq (, $(findstring darwin, $(OS)))
|
||||
# $(CC) -dynamiclib $(LDFLAGS) -Wl,-install_name,$(SONAME) -o $@ $^
|
||||
$(CC) -dynamiclib $(LDFLAGS) -Wl,-install_name,$(INSTALLDIRLIB)/$(SONAME) -o $@ $^
|
||||
else
|
||||
$(CC) -shared $(LDFLAGS) -Wl,-soname,$(SONAME) -o $@ $^
|
||||
endif
|
||||
|
||||
$(BUILD)/%.o: $(SRCDIR)/%.c $(BUILD)/%.d
|
||||
$(CC) $(CPPFLAGS) $(CFLAGS) -c -o $@ $<
|
||||
|
||||
$(DEPS): $(BUILDINCLUDES)
|
||||
|
||||
$(BUILD)/%.d: $(SRCDIR)/%.c
|
||||
@$(MKDIR) $(@D)
|
||||
$(CC) $(CPPFLAGS) $(DEPFLAGS) -MT $@ -o $@ $<
|
||||
|
||||
$(BUILD)/upnpc-static: $(BUILD)/upnpc.o $(LIBRARY)
|
||||
$(CC) $(LDFLAGS) -o $@ $^ $(LOADLIBES) $(LDLIBS)
|
||||
|
||||
$(BUILD)/upnpc-shared: $(BUILD)/upnpc.o $(SHAREDLIBRARY)
|
||||
$(CC) $(LDFLAGS) -o $@ $^ $(LOADLIBES) $(LDLIBS)
|
||||
|
||||
$(BUILD)/upnp-listdevices-static: $(BUILD)/listdevices.o $(LIBRARY)
|
||||
$(CC) $(LDFLAGS) -o $@ $^ $(LOADLIBES) $(LDLIBS)
|
||||
|
||||
$(BUILD)/upnp-listdevices-shared: $(BUILD)/listdevices.o $(SHAREDLIBRARY)
|
||||
$(CC) $(LDFLAGS) -o $@ $^ $(LOADLIBES) $(LDLIBS)
|
||||
|
||||
$(BUILD)/testminixml: $(TESTMINIXMLOBJS)
|
||||
|
||||
$(BUILD)/testminiwget: $(TESTMINIWGETOBJS)
|
||||
|
||||
$(BUILD)/minixmlvalid: $(addprefix $(BUILD)/, minixml.o minixmlvalid.o)
|
||||
|
||||
$(BUILD)/testupnpreplyparse: $(TESTUPNPREPLYPARSE)
|
||||
|
||||
$(BUILD)/testigddescparse: $(TESTIGDDESCPARSE)
|
||||
|
||||
$(BUILD)/testportlistingparse: $(TESTPORTLISTINGPARSE)
|
||||
|
||||
$(BUILD)/testaddr_is_reserved: $(TESTADDR_IS_RESERVED)
|
||||
|
||||
$(BUILD)/miniupnpcstrings.h: miniupnpcstrings.h.in updateminiupnpcstrings.sh VERSION
|
||||
@$(MKDIR) $(@D)
|
||||
$(SH) updateminiupnpcstrings.sh $@ $<
|
||||
|
||||
# ftp tool supplied with OpenBSD can download files from http.
|
||||
jnaerator-%.jar:
|
||||
wget $(JNAERATORBASEURL)/$@ || \
|
||||
curl -o $@ $(JNAERATORBASEURL)/$@ || \
|
||||
ftp $(JNAERATORBASEURL)/$@
|
||||
|
||||
jar: $(SHAREDLIBRARY) $(JNAERATOR)
|
||||
$(JAVA) -jar $(JNAERATOR) $(JNAERATORARGS) \
|
||||
miniupnpc.h miniupnpc_declspec.h upnpcommands.h upnpreplyparse.h \
|
||||
igd_desc_parse.h miniwget.h upnperrors.h $(SHAREDLIBRARY) \
|
||||
-package fr.free.miniupnp -o . -jar java/miniupnpc_$(JARSUFFIX).jar -v
|
||||
|
||||
mvn_install:
|
||||
mvn install:install-file -Dfile=java/miniupnpc_$(JARSUFFIX).jar \
|
||||
-DgroupId=com.github \
|
||||
-DartifactId=miniupnp \
|
||||
-Dversion=$(VERSION) \
|
||||
-Dpackaging=jar \
|
||||
-Dclassifier=$(JARSUFFIX) \
|
||||
-DgeneratePom=true \
|
||||
-DcreateChecksum=true
|
||||
|
||||
# make .deb packages
|
||||
deb: /usr/share/pyshared/stdeb all
|
||||
(python setup.py --command-packages=stdeb.command bdist_deb)
|
||||
|
||||
# install .deb packages
|
||||
ideb:
|
||||
(sudo dpkg -i deb_dist/*.deb)
|
||||
|
||||
/usr/share/pyshared/stdeb: /usr/share/doc/python-all-dev
|
||||
(sudo apt-get install python-stdeb)
|
||||
|
||||
/usr/share/doc/python-all-dev:
|
||||
(sudo apt-get install python-all-dev)
|
||||
|
||||
minihttptestserver: minihttptestserver.o
|
||||
|
||||
doxygen/html/index.html: Doxyfile $(SRCS) $(HEADERS) $(PRIVATE_HEADERS)
|
||||
doxygen
|
||||
|
||||
print-%:
|
||||
@echo "$* = $($*)"
|
||||
|
||||
ifneq ($(MAKECMDGOALS),clean)
|
||||
-include $(DEPS)
|
||||
endif
|
||||
@@ -0,0 +1,188 @@
|
||||
# $Id: Makefile.mingw,v 1.37 2024/05/16 00:10:25 nanard Exp $
|
||||
# Miniupnp project.
|
||||
# http://miniupnp.free.fr/ or https://miniupnp.tuxfamily.org/
|
||||
# (c) 2005-2024 Thomas Bernard
|
||||
# This Makefile is made for MinGW
|
||||
#
|
||||
# To cross compile on a *nix machine :
|
||||
# make -f Makefile.mingw DLLWRAP=mingw32-dllwrap CC=mingw32-gcc AR=mingw32-ar WINDRES=mingw32-windres
|
||||
#
|
||||
SRCDIR = src
|
||||
INCDIR = include
|
||||
|
||||
CC ?= gcc
|
||||
SETUP_COMPILER_FLAG?=
|
||||
DLLWRAP = dllwrap
|
||||
WINDRES = windres
|
||||
SH = /bin/sh
|
||||
ZIP = zip
|
||||
ifeq ($(OS),Windows_NT)
|
||||
RM = del
|
||||
else
|
||||
RM = rm -f
|
||||
endif
|
||||
|
||||
CFLAGS ?= -Os
|
||||
CFLAGS += -Wall
|
||||
CFLAGS += -W -Wstrict-prototypes
|
||||
|
||||
CPPFLAGS += -DNDEBUG -D_WIN32_WINNT=0x501
|
||||
CPPFLAGS += -Iinclude
|
||||
CPPFLAGS += -I.
|
||||
|
||||
# -liphlpapi is needed for GetBestRoute() and GetIpAddrTable()
|
||||
LDLIBS = -lws2_32 -liphlpapi
|
||||
|
||||
PYTHON=\utils\python25\python
|
||||
OBJS=miniwget.o minixml.o igd_desc_parse.o minisoap.o \
|
||||
minissdpc.o \
|
||||
miniupnpc.o upnpreplyparse.o upnpcommands.o upnperrors.o \
|
||||
connecthostport.o portlistingparse.o receivedata.o \
|
||||
upnpdev.o addr_is_reserved.o
|
||||
OBJSDLL=$(addprefix dll-, $(OBJS)) winres.o
|
||||
BINARIES=upnpc-static.exe upnpc-shared.exe \
|
||||
upnp-listdevices-static.exe upnp-listdevices-shared.exe \
|
||||
miniupnpc.dll libminiupnpc.a \
|
||||
testminixml.exe
|
||||
ifneq ($(GITHUB_SHA),)
|
||||
COMMITREF=$(GITHUB_SHA)
|
||||
else
|
||||
ifneq ($(CI_COMMIT_SHORT_SHA),)
|
||||
COMMITREF=$(CI_COMMIT_SHORT_SHA)
|
||||
else
|
||||
COMMITREF=$(shell git rev-parse --short HEAD)
|
||||
endif
|
||||
endif
|
||||
DISTFILE:=$(shell echo "miniupnpc-bin-win32-`cat VERSION`-$(COMMITREF).zip")
|
||||
|
||||
LIBDIR ?= lib
|
||||
# install directories
|
||||
ifeq ($(strip $(PREFIX)),)
|
||||
INSTALLPREFIX ?= /usr
|
||||
else
|
||||
INSTALLPREFIX ?= $(PREFIX)
|
||||
endif
|
||||
|
||||
.PHONY: all dist clean
|
||||
|
||||
all: $(BINARIES)
|
||||
|
||||
dist: $(DISTFILE)
|
||||
|
||||
clean:
|
||||
$(RM) miniupnpcstrings.h
|
||||
$(RM) *.o
|
||||
$(RM) *.exe
|
||||
$(RM) miniupnpc.dll miniupnpc.lib miniupnpc.dll.def
|
||||
$(RM) libminiupnpc.a
|
||||
$(RM) $(DISTFILE)
|
||||
|
||||
$(DISTFILE): $(BINARIES)
|
||||
$(ZIP) $@ *.exe *.dll *.lib *.def *.a LICENSE README Changelog.txt
|
||||
|
||||
libminiupnpc.a: $(OBJS)
|
||||
$(AR) cr $@ $?
|
||||
|
||||
pythonmodule: libminiupnpc.a
|
||||
$(PYTHON) setupmingw32.py build $(SETUP_COMPILER_FLAG)
|
||||
$(PYTHON) setupmingw32.py install --skip-build
|
||||
$(PYTHON) setupmingw32.py sdist
|
||||
$(PYTHON) setupmingw32.py bdist_wheel --skip-build
|
||||
|
||||
miniupnpc.dll: miniupnpc.def $(OBJSDLL)
|
||||
$(DLLWRAP) -k --driver-name $(CC) \
|
||||
--def miniupnpc.def \
|
||||
--output-def miniupnpc.dll.def \
|
||||
--implib miniupnpc.lib -o $@ \
|
||||
$(OBJSDLL) $(LDLIBS)
|
||||
|
||||
miniupnpc.lib: miniupnpc.dll
|
||||
|
||||
%.o: $(SRCDIR)/%.c
|
||||
$(CC) $(CFLAGS) $(CPPFLAGS) -DMINIUPNP_STATICLIB -c -o $@ $<
|
||||
|
||||
dll-%.o: $(SRCDIR)/%.c
|
||||
$(CC) $(CFLAGS) $(CPPFLAGS) -DMINIUPNP_EXPORTS -c -o $@ $<
|
||||
|
||||
%-shared.o: $(SRCDIR)/%.c
|
||||
$(CC) $(CFLAGS) $(CPPFLAGS) -c -o $@ $<
|
||||
|
||||
# --enable-stdcall-fixup
|
||||
%-static.exe: %.o libminiupnpc.a
|
||||
$(CC) -static -o $@ $^ $(LDLIBS)
|
||||
|
||||
%-shared.exe: %-shared.o miniupnpc.lib
|
||||
$(CC) -o $@ $^ $(LDLIBS)
|
||||
|
||||
upnp-listdevices-static.exe: listdevices.o libminiupnpc.a
|
||||
$(CC) -static -o $@ $^ $(LDLIBS)
|
||||
|
||||
upnp-listdevices-shared.exe: listdevices-shared.o miniupnpc.lib
|
||||
$(CC) -o $@ $^ $(LDLIBS)
|
||||
|
||||
# To make miniupnpcstrings.h from miniupnpcstrings.h.in we either
|
||||
# use a custom executable (if running make under windows) or use
|
||||
# sed (if cross compiling from another platform).
|
||||
ifeq ($(OS),Windows_NT)
|
||||
wingenminiupnpcstrings.exe: wingenminiupnpcstrings.c
|
||||
$(CC) $(CFLAGS) $(CPPFLAGS) -o $@ $^
|
||||
|
||||
miniupnpcstrings.h: miniupnpcstrings.h.in wingenminiupnpcstrings.exe VERSION
|
||||
.\wingenminiupnpcstrings.exe $< $@ rc_version.h
|
||||
|
||||
rc_version.h: miniupnpcstrings.h
|
||||
else
|
||||
miniupnpcstrings.h: miniupnpcstrings.h.in VERSION
|
||||
sed 's|OS_STRING ".*"|OS_STRING "Windows/Mingw32"|' $< | \
|
||||
sed 's|MINIUPNPC_VERSION_STRING ".*"|MINIUPNPC_VERSION_STRING "$(shell cat VERSION)"|' > $@
|
||||
|
||||
rc_version.h: VERSION
|
||||
echo "#define LIBMINIUPNPC_DOTTED_VERSION \"$(shell cat VERSION)\"" > $@.tmp
|
||||
echo "#define LIBMINIUPNPC_MAJOR_VERSION $(shell cat VERSION|cut -d. -f1)" >> $@.tmp
|
||||
echo "#define LIBMINIUPNPC_MINOR_VERSION $(shell cat VERSION|cut -d. -f2)" >> $@.tmp
|
||||
echo "#define LIBMINIUPNPC_MICRO_VERSION $(shell cat VERSION|cut -d. -f3)" >> $@.tmp
|
||||
mv $@.tmp $@
|
||||
endif
|
||||
|
||||
miniupnpc.pc: VERSION
|
||||
$(RM) $@
|
||||
echo "prefix=$(INSTALLPREFIX)" >> $@
|
||||
echo "exec_prefix=\$${prefix}" >> $@
|
||||
echo "libdir=\$${exec_prefix}/$(LIBDIR)" >> $@
|
||||
echo "includedir=\$${prefix}/include" >> $@
|
||||
echo "" >> $@
|
||||
echo "Name: miniUPnPc" >> $@
|
||||
echo "Description: UPnP IGD client lightweight library" >> $@
|
||||
echo "Version: $(shell cat VERSION)" >> $@
|
||||
echo "Libs: -L\$${libdir} -lminiupnpc" >> $@
|
||||
echo "Cflags: -I\$${includedir}" >> $@
|
||||
|
||||
winres.o: miniupnpc.rc rc_version.h
|
||||
$(WINDRES) -D INTERNAL_NAME=\\\"miniupnpc.dll\\0\\\" -i $< -o $@
|
||||
|
||||
testminixml.exe: testminixml.o minixml.o igd_desc_parse.o
|
||||
$(CC) -static -o $@ $^
|
||||
|
||||
minixml.o: $(SRCDIR)/minixml.c $(SRCDIR)/minixml.h
|
||||
|
||||
upnpc.o: include/miniwget.h $(SRCDIR)/minisoap.h include/miniupnpc.h include/igd_desc_parse.h
|
||||
upnpc.o: include/upnpreplyparse.h include/upnpcommands.h include/upnperrors.h miniupnpcstrings.h
|
||||
|
||||
miniwget.o: $(SRCDIR)/miniwget.c include/miniwget.h miniupnpcstrings.h $(SRCDIR)/connecthostport.h
|
||||
|
||||
minisoap.o: $(SRCDIR)/minisoap.c $(SRCDIR)/minisoap.h miniupnpcstrings.h
|
||||
|
||||
miniupnpc.o: $(SRCDIR)/miniupnpc.c include/miniupnpc.h $(SRCDIR)/minisoap.h \
|
||||
include/miniwget.h $(SRCDIR)/minixml.h $(SRCDIR)/addr_is_reserved.h
|
||||
|
||||
igd_desc_parse.o: $(SRCDIR)/igd_desc_parse.c include/igd_desc_parse.h
|
||||
|
||||
upnpreplyparse.o: $(SRCDIR)/upnpreplyparse.c include/upnpreplyparse.h $(SRCDIR)/minixml.h
|
||||
|
||||
upnpcommands.o: $(SRCDIR)/upnpcommands.c include/upnpcommands.h include/upnpreplyparse.h \
|
||||
include/miniupnpc.h include/portlistingparse.h
|
||||
|
||||
minissdpc.o: $(SRCDIR)/minissdpc.c $(SRCDIR)/minissdpc.h $(SRCDIR)/receivedata.h
|
||||
|
||||
upnpdev.o: $(SRCDIR)/upnpdev.c include/upnpdev.h
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
Project: miniupnp
|
||||
Project web page: http://miniupnp.free.fr/ or https://miniupnp.tuxfamily.org/
|
||||
github: https://github.com/miniupnp/miniupnp
|
||||
Author: Thomas Bernard
|
||||
Copyright (c) 2005-2025 Thomas Bernard
|
||||
This software is subject to the conditions detailed in the
|
||||
LICENSE file provided within this distribution.
|
||||
|
||||
|
||||
* miniUPnP Client - miniUPnPc *
|
||||
|
||||
To compile, simply run 'gmake' (could be 'make' on your system).
|
||||
Under win32, to compile with MinGW, type "mingw32make.bat".
|
||||
MS Visual C solution and project files are supplied in the msvc/ subdirectory.
|
||||
The miniupnpc library is available as a static library or as a DLL :
|
||||
define MINIUPNP_STATICLIB if you want to link against the static library.
|
||||
|
||||
The compilation is known to work under linux, FreeBSD,
|
||||
OpenBSD, MacOS X, AmigaOS and cygwin.
|
||||
The official AmigaOS4.1 SDK was used for AmigaOS4 and GeekGadgets for AmigaOS3.
|
||||
upx (http://upx.sourceforge.net) is used to compress the win32 .exe files.
|
||||
|
||||
To install the library and headers on the system use :
|
||||
> su
|
||||
> make install
|
||||
> exit
|
||||
|
||||
alternatively, to install into a specific location, use :
|
||||
> INSTALLPREFIX=/usr/local make install
|
||||
|
||||
upnpc.c is a sample client using the libminiupnpc.
|
||||
To use the libminiupnpc in your application, link it with
|
||||
libminiupnpc.a (or .so) and use the following functions found in miniupnpc.h,
|
||||
upnpcommands.h and miniwget.h :
|
||||
- upnpDiscover()
|
||||
- UPNP_GetValidIGD()
|
||||
- miniwget()
|
||||
- parserootdesc()
|
||||
- GetUPNPUrls()
|
||||
- UPNP_* (calling UPNP methods)
|
||||
|
||||
Note : use #include <miniupnpc/miniupnpc.h> etc... for the includes
|
||||
and -lminiupnpc for the link
|
||||
|
||||
Discovery process is speeded up when MiniSSDPd is running on the machine.
|
||||
|
||||
|
||||
* Python module *
|
||||
|
||||
you can build a python module with 'make pythonmodule'
|
||||
and install it with 'make installpythonmodule'.
|
||||
setup.py (and setupmingw32.py) are included in the distribution.
|
||||
|
||||
|
||||
Feel free to contact me if you have any problem :
|
||||
e-mail : [email protected]
|
||||
|
||||
If you are using libminiupnpc in your application, please
|
||||
send me an email !
|
||||
|
||||
For any question, you can use the web forum :
|
||||
https://miniupnp.tuxfamily.org/forum/
|
||||
|
||||
Bugs should be reported on GitHub :
|
||||
https://github.com/miniupnp/miniupnp/issues
|
||||
|
||||
* Linux firewall configuration for UPnP clients *
|
||||
|
||||
Due to how UPnP protocol is designed, unicast responses to UPnP multicast client
|
||||
requests are not tracked by Linux netfilter. And therefore netfilter executes
|
||||
default action for them (which is in most cases DROP response packet).
|
||||
|
||||
To workaround this limitation, custom ipset hash table can be used. It is
|
||||
supported since Linux kernel >= 2.6.39.
|
||||
|
||||
Rules for IPv4:
|
||||
$ ipset create upnp hash:ip,port timeout 3
|
||||
$ iptables -A OUTPUT -d 239.255.255.250/32 -p udp -m udp --dport 1900 -j SET --add-set upnp src,src --exist
|
||||
$ iptables -A INPUT -p udp -m set --match-set upnp dst,dst -j ACCEPT
|
||||
$ iptables -A INPUT -d 239.255.255.250/32 -p udp -m udp --dport 1900 -j ACCEPT
|
||||
|
||||
Rules for IPv6:
|
||||
$ ipset create upnp6 hash:ip,port timeout 3 family inet6
|
||||
$ ip6tables -A OUTPUT -d ff02::c/128 -p udp -m udp --dport 1900 -j SET --add-set upnp6 src,src --exist
|
||||
$ ip6tables -A OUTPUT -d ff05::c/128 -p udp -m udp --dport 1900 -j SET --add-set upnp6 src,src --exist
|
||||
$ ip6tables -A INPUT -p udp -m set --match-set upnp6 dst,dst -j ACCEPT
|
||||
$ ip6tables -A INPUT -d ff02::c/128 -p udp -m udp --dport 1900 -j ACCEPT
|
||||
$ ip6tables -A INPUT -d ff05::c/128 -p udp -m udp --dport 1900 -j ACCEPT
|
||||
|
||||
Detailed description is available on:
|
||||
https://serverfault.com/a/911286
|
||||
https://unix.stackexchange.com/a/444804
|
||||
@@ -0,0 +1 @@
|
||||
2.3.3
|
||||
@@ -0,0 +1,90 @@
|
||||
/* $Id: addr_is_reserved.c,v 1.8 2025/05/25 21:56:48 nanard Exp $ */
|
||||
/* vim: tabstop=4 shiftwidth=4 noexpandtab
|
||||
* Project : miniupnp
|
||||
* Web : http://miniupnp.free.fr/ or https://miniupnp.tuxfamily.org/
|
||||
* Author : Thomas BERNARD
|
||||
* copyright (c) 2005-2025 Thomas Bernard
|
||||
* This software is subjet to the conditions detailed in the
|
||||
* provided LICENSE file. */
|
||||
#ifdef _WIN32
|
||||
/* Win32 Specific includes and defines */
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <winsock2.h>
|
||||
#include <ws2tcpip.h>
|
||||
#if !defined(_MSC_VER)
|
||||
#include <stdint.h>
|
||||
#else /* !defined(_MSC_VER) */
|
||||
typedef unsigned long uint32_t;
|
||||
#endif /* !defined(_MSC_VER) */
|
||||
#if !defined(_WIN32_WINNT_VISTA)
|
||||
#define _WIN32_WINNT_VISTA 0x0600
|
||||
#endif
|
||||
#else /* _WIN32 */
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#endif /* _WIN32 */
|
||||
#ifdef DEBUG
|
||||
#include <stdio.h>
|
||||
#endif
|
||||
|
||||
/* List of IP address blocks which are private / reserved and therefore not suitable for public external IP addresses */
|
||||
#define IP(a, b, c, d) (((a) << 24) + ((b) << 16) + ((c) << 8) + (d))
|
||||
#define MSK(m) (32-(m))
|
||||
static const struct { uint32_t address; uint32_t rmask; } reserved[] = {
|
||||
{ IP( 0, 0, 0, 0), MSK( 8) }, /* RFC1122 "This host on this network" */
|
||||
{ IP( 10, 0, 0, 0), MSK( 8) }, /* RFC1918 Private-Use */
|
||||
{ IP(100, 64, 0, 0), MSK(10) }, /* RFC6598 Shared Address Space */
|
||||
{ IP(127, 0, 0, 0), MSK( 8) }, /* RFC1122 Loopback */
|
||||
{ IP(169, 254, 0, 0), MSK(16) }, /* RFC3927 Link-Local */
|
||||
{ IP(172, 16, 0, 0), MSK(12) }, /* RFC1918 Private-Use */
|
||||
{ IP(192, 0, 0, 0), MSK(24) }, /* RFC6890 IETF Protocol Assignments */
|
||||
{ IP(192, 0, 2, 0), MSK(24) }, /* RFC5737 Documentation (TEST-NET-1) */
|
||||
{ IP(192, 31, 196, 0), MSK(24) }, /* RFC7535 AS112-v4 */
|
||||
{ IP(192, 52, 193, 0), MSK(24) }, /* RFC7450 AMT */
|
||||
{ IP(192, 88, 99, 0), MSK(24) }, /* RFC7526 6to4 Relay Anycast */
|
||||
{ IP(192, 168, 0, 0), MSK(16) }, /* RFC1918 Private-Use */
|
||||
{ IP(192, 175, 48, 0), MSK(24) }, /* RFC7534 Direct Delegation AS112 Service */
|
||||
{ IP(198, 18, 0, 0), MSK(15) }, /* RFC2544 Benchmarking */
|
||||
{ IP(198, 51, 100, 0), MSK(24) }, /* RFC5737 Documentation (TEST-NET-2) */
|
||||
{ IP(203, 0, 113, 0), MSK(24) }, /* RFC5737 Documentation (TEST-NET-3) */
|
||||
{ IP(224, 0, 0, 0), MSK( 4) }, /* RFC1112 Multicast */
|
||||
{ IP(240, 0, 0, 0), MSK( 4) }, /* RFC1112 Reserved for Future Use + RFC919 Limited Broadcast */
|
||||
};
|
||||
#undef IP
|
||||
#undef MSK
|
||||
|
||||
/**
|
||||
* @return 1 or 0
|
||||
*/
|
||||
int addr_is_reserved(const char * addr_str)
|
||||
{
|
||||
uint32_t addr_n, address;
|
||||
size_t i;
|
||||
|
||||
#if defined(_WIN32) && (_WIN32_WINNT < _WIN32_WINNT_VISTA)
|
||||
addr_n = inet_addr(addr_str);
|
||||
if (addr_n == INADDR_NONE)
|
||||
return 1;
|
||||
#else
|
||||
/* was : addr_n = inet_addr(addr_str); */
|
||||
if (inet_pton(AF_INET, addr_str, &addr_n) <= 0) {
|
||||
/* error */
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
address = ntohl(addr_n);
|
||||
|
||||
for (i = 0; i < sizeof(reserved)/sizeof(reserved[0]); ++i) {
|
||||
if ((address >> reserved[i].rmask) == (reserved[i].address >> reserved[i].rmask)) {
|
||||
#ifdef DEBUG
|
||||
printf("IP address %s is reserved\n", addr_str);
|
||||
#endif
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
/* $Id: addr_is_reserved.h,v 1.1 2020/09/28 21:11:19 nanard Exp $ */
|
||||
/* vim: tabstop=4 shiftwidth=4 noexpandtab
|
||||
* Project: miniupnp
|
||||
* http://miniupnp.free.fr/ or https://miniupnp.tuxfamily.org/
|
||||
* Author: Thomas Bernard
|
||||
* Copyright (c) 2005-2020 Thomas Bernard
|
||||
* This software is subjects to the conditions detailed
|
||||
* in the LICENCE file provided within this distribution */
|
||||
#ifndef ADDR_IS_RESERVED_H_INCLUDED
|
||||
#define ADDR_IS_RESERVED_H_INCLUDED
|
||||
|
||||
int addr_is_reserved(const char * addr_str);
|
||||
|
||||
#endif /* ADDR_IS_RESERVED_H_INCLUDED */
|
||||
@@ -0,0 +1,204 @@
|
||||
$Id: apiversions.txt,v 1.15 2025/03/18 23:40:14 nanard Exp $
|
||||
|
||||
Differences in API between miniUPnPc versions
|
||||
|
||||
API version 21
|
||||
Add UPNP_GetSpecificPortMappingEntryExt() and
|
||||
UPNP_GetGenericPortMappingEntryExt() functions to support retrieving
|
||||
arbitrary long descriptions.
|
||||
updated macro :
|
||||
#define MINIUPNPC_API_VERSION 21
|
||||
|
||||
API version 20
|
||||
cleanup : remove STRTOUI and parseURL() from public headers
|
||||
remove struct NameValue from upnpreplyparse.h
|
||||
updated macro :
|
||||
#define MINIUPNPC_API_VERSION 20
|
||||
This more a cleanup than a real API change. Things that were removed
|
||||
from public headers were not supposed to be used and/or not
|
||||
exposed in the dll export list.
|
||||
|
||||
API version 19
|
||||
change simpleUPnPcommand() prototype
|
||||
updated macro :
|
||||
#define MINIUPNPC_API_VERSION 19
|
||||
|
||||
API version 18
|
||||
change UPNP_GetValidIGD() prototype and return values
|
||||
updated macro :
|
||||
#define MINIUPNPC_API_VERSION 18
|
||||
|
||||
API version 17
|
||||
change struct UPNPDev
|
||||
move getHTTPResponse() to miniwget_private.h
|
||||
updated macro :
|
||||
#define MINIUPNPC_API_VERSION 17
|
||||
|
||||
API version 16
|
||||
added "status_code" argument to getHTTPResponse(), miniwget() and miniwget_getaddr()
|
||||
updated macro :
|
||||
#define MINIUPNPC_API_VERSION 16
|
||||
|
||||
API version 15
|
||||
changed "sameport" argument of upnpDiscover() upnpDiscoverAll() upnpDiscoverDevice()
|
||||
to "localport". When 0 or 1, behaviour is not changed, but it can take
|
||||
any other value between 2 and 65535
|
||||
Existing programs should be compatible
|
||||
updated macro :
|
||||
#define MINIUPNPC_API_VERSION 15
|
||||
|
||||
API version 14
|
||||
miniupnpc.h
|
||||
add ttl argument to upnpDiscover() upnpDiscoverAll() upnpDiscoverDevice()
|
||||
upnpDiscoverDevices()
|
||||
getDevicesFromMiniSSDPD() :
|
||||
connectToMiniSSDPD() / disconnectFromMiniSSDPD()
|
||||
requestDevicesFromMiniSSDPD() / receiveDevicesFromMiniSSDPD()
|
||||
updated macro :
|
||||
#define MINIUPNPC_API_VERSION 14
|
||||
|
||||
API version 13
|
||||
miniupnpc.h:
|
||||
add searchalltype param to upnpDiscoverDevices() function
|
||||
updated macro :
|
||||
#define MINIUPNPC_API_VERSION 13
|
||||
|
||||
API version 12
|
||||
miniupnpc.h :
|
||||
add upnpDiscoverAll() / upnpDiscoverDevice() / upnpDiscoverDevices()
|
||||
functions
|
||||
updated macros :
|
||||
#define MINIUPNPC_API_VERSION 12
|
||||
|
||||
API version 11
|
||||
|
||||
upnpreplyparse.h / portlistingparse.h :
|
||||
removed usage of sys/queue.h / bsdqueue.h
|
||||
|
||||
miniupnpc.h:
|
||||
updated macros :
|
||||
#define MINIUPNPC_API_VERSION 11
|
||||
|
||||
====================== miniUPnPc version 1.9 ======================
|
||||
API version 10
|
||||
|
||||
upnpcommands.h:
|
||||
added argument remoteHost to UPNP_GetSpecificPortMappingEntry()
|
||||
|
||||
miniupnpc.h:
|
||||
updated macros :
|
||||
#define MINIUPNPC_VERSION "1.9"
|
||||
#define MINIUPNPC_API_VERSION 10
|
||||
|
||||
====================== miniUPnPc version 1.8 ======================
|
||||
API version 9
|
||||
|
||||
miniupnpc.h:
|
||||
updated macros :
|
||||
#define MINIUPNPC_VERSION "1.8"
|
||||
#define MINIUPNPC_API_VERSION 9
|
||||
added "unsigned int scope_id;" to struct UPNPDev
|
||||
added scope_id argument to GetUPNPUrls()
|
||||
|
||||
|
||||
|
||||
====================== miniUPnPc version 1.7 ======================
|
||||
API version 8
|
||||
|
||||
miniupnpc.h :
|
||||
add new macros :
|
||||
#define MINIUPNPC_VERSION "1.7"
|
||||
#define MINIUPNPC_API_VERSION 8
|
||||
add rootdescURL to struct UPNPUrls
|
||||
|
||||
|
||||
|
||||
====================== miniUPnPc version 1.6 ======================
|
||||
API version 8
|
||||
|
||||
Adding support for IPv6.
|
||||
igd_desc_parse.h :
|
||||
struct IGDdatas_service :
|
||||
add char presentationurl[MINIUPNPC_URL_MAXSIZE];
|
||||
struct IGDdatas :
|
||||
add struct IGDdatas_service IPv6FC;
|
||||
miniupnpc.h :
|
||||
new macros :
|
||||
#define UPNPDISCOVER_SUCCESS (0)
|
||||
#define UPNPDISCOVER_UNKNOWN_ERROR (-1)
|
||||
#define UPNPDISCOVER_SOCKET_ERROR (-101)
|
||||
#define UPNPDISCOVER_MEMORY_ERROR (-102)
|
||||
simpleUPnPcommand() prototype changed (but is normaly not used by API users)
|
||||
add arguments ipv6 and error to upnpDiscover() :
|
||||
struct UPNPDev *
|
||||
upnpDiscover(int delay, const char * multicastif,
|
||||
const char * minissdpdsock, int sameport,
|
||||
int ipv6,
|
||||
int * error);
|
||||
add controlURL_6FC member to struct UPNPUrls :
|
||||
struct UPNPUrls {
|
||||
char * controlURL;
|
||||
char * ipcondescURL;
|
||||
char * controlURL_CIF;
|
||||
char * controlURL_6FC;
|
||||
};
|
||||
|
||||
upnpcommands.h :
|
||||
add leaseDuration argument to UPNP_AddPortMapping()
|
||||
add desc, enabled and leaseDuration arguments to UPNP_GetSpecificPortMappingEntry()
|
||||
add UPNP_GetListOfPortMappings() function (IGDv2)
|
||||
add IGDv2 IPv6 related functions :
|
||||
UPNP_GetFirewallStatus()
|
||||
UPNP_GetOutboundPinholeTimeout()
|
||||
UPNP_AddPinhole()
|
||||
UPNP_UpdatePinhole()
|
||||
UPNP_DeletePinhole()
|
||||
UPNP_CheckPinholeWorking()
|
||||
UPNP_GetPinholePackets()
|
||||
|
||||
|
||||
|
||||
====================== miniUPnPc version 1.5 ======================
|
||||
API version 5
|
||||
|
||||
new function :
|
||||
int UPNPIGD_IsConnected(struct UPNPUrls *, struct IGDdatas *);
|
||||
new macro in upnpcommands.h :
|
||||
#define UPNPCOMMAND_HTTP_ERROR
|
||||
|
||||
====================== miniUPnPc version 1.4 ======================
|
||||
Same API as version 1.3
|
||||
|
||||
====================== miniUPnPc version 1.3 ======================
|
||||
API version 4
|
||||
|
||||
Use UNSIGNED_INTEGER type for
|
||||
UPNP_GetTotalBytesSent(), UPNP_GetTotalBytesReceived(),
|
||||
UPNP_GetTotalPacketsSent(), UPNP_GetTotalPacketsReceived()
|
||||
Add remoteHost argument to UPNP_AddPortMapping() and UPNP_DeletePortMapping()
|
||||
|
||||
====================== miniUPnPc version 1.2 ======================
|
||||
API version 3
|
||||
|
||||
added sameport argument to upnpDiscover()
|
||||
struct UPNPDev *
|
||||
upnpDiscover(int delay, const char * multicastif,
|
||||
const char * minissdpdsock, int sameport);
|
||||
|
||||
====================== miniUPnPc Version 1.1 ======================
|
||||
Same API as 1.0
|
||||
|
||||
|
||||
====================== miniUPnPc Version 1.0 ======================
|
||||
API version 2
|
||||
|
||||
|
||||
struct UPNPDev {
|
||||
struct UPNPDev * pNext;
|
||||
char * descURL;
|
||||
char * st;
|
||||
char buffer[2];
|
||||
};
|
||||
struct UPNPDev * upnpDiscover(int delay, const char * multicastif,
|
||||
const char * minissdpdsock);
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
/* $Id: codelength.h,v 1.5 2015/07/09 12:40:18 nanard Exp $ */
|
||||
/* Project : miniupnp
|
||||
* Author : Thomas BERNARD
|
||||
* copyright (c) 2005-2015 Thomas Bernard
|
||||
* This software is subjet to the conditions detailed in the
|
||||
* provided LICENCE file. */
|
||||
#ifndef CODELENGTH_H_INCLUDED
|
||||
#define CODELENGTH_H_INCLUDED
|
||||
|
||||
/* Encode length by using 7bit per Byte :
|
||||
* Most significant bit of each byte specifies that the
|
||||
* following byte is part of the code */
|
||||
|
||||
/* n : unsigned
|
||||
* p : unsigned char *
|
||||
*/
|
||||
#define DECODELENGTH(n, p) n = 0; \
|
||||
do { n = (n << 7) | (*p & 0x7f); } \
|
||||
while((*(p++)&0x80) && (n<(1<<25)));
|
||||
|
||||
/* n : unsigned
|
||||
* READ : function/macro to read one byte (unsigned char)
|
||||
*/
|
||||
#define DECODELENGTH_READ(n, READ) \
|
||||
n = 0; \
|
||||
do { \
|
||||
unsigned char c; \
|
||||
READ(c); \
|
||||
n = (n << 7) | (c & 0x07f); \
|
||||
if(!(c&0x80)) break; \
|
||||
} while(n<(1<<25));
|
||||
|
||||
/* n : unsigned
|
||||
* p : unsigned char *
|
||||
* p_limit : unsigned char *
|
||||
*/
|
||||
#define DECODELENGTH_CHECKLIMIT(n, p, p_limit) \
|
||||
n = 0; \
|
||||
do { \
|
||||
if((p) >= (p_limit)) break; \
|
||||
n = (n << 7) | (*(p) & 0x7f); \
|
||||
} while((*((p)++)&0x80) && (n<(1<<25)));
|
||||
|
||||
|
||||
/* n : unsigned
|
||||
* p : unsigned char *
|
||||
*/
|
||||
#define CODELENGTH(n, p) if(n>=268435456) *(p++) = (n >> 28) | 0x80; \
|
||||
if(n>=2097152) *(p++) = (n >> 21) | 0x80; \
|
||||
if(n>=16384) *(p++) = (n >> 14) | 0x80; \
|
||||
if(n>=128) *(p++) = (n >> 7) | 0x80; \
|
||||
*(p++) = n & 0x7f;
|
||||
|
||||
#endif /* CODELENGTH_H_INCLUDED */
|
||||
@@ -0,0 +1,290 @@
|
||||
/* $Id: connecthostport.c,v 1.26 2025/05/25 21:56:48 nanard Exp $ */
|
||||
/* vim: tabstop=4 shiftwidth=4 noexpandtab
|
||||
* Project : miniupnp
|
||||
* Web : http://miniupnp.free.fr/ or https://miniupnp.tuxfamily.org/
|
||||
* Author : Thomas Bernard
|
||||
* Copyright (c) 2010-2025 Thomas Bernard
|
||||
* This software is subject to the conditions detailed in the
|
||||
* LICENCE file provided in this distribution. */
|
||||
|
||||
/* use getaddrinfo() or gethostbyname()
|
||||
* uncomment the following line in order to use gethostbyname() */
|
||||
#ifdef NO_GETADDRINFO
|
||||
#define USE_GETHOSTBYNAME
|
||||
#endif
|
||||
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#ifdef _WIN32
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <winsock2.h>
|
||||
#include <ws2tcpip.h>
|
||||
#include <io.h>
|
||||
#define MAXHOSTNAMELEN 64
|
||||
#include "win32_snprintf.h"
|
||||
#define herror
|
||||
#define socklen_t int
|
||||
#else /* #ifdef _WIN32 */
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#ifdef MINIUPNPC_SET_SOCKET_TIMEOUT
|
||||
#include <sys/time.h>
|
||||
#endif /* #ifdef MINIUPNPC_SET_SOCKET_TIMEOUT */
|
||||
#include <sys/param.h>
|
||||
#include <sys/select.h>
|
||||
#include <errno.h>
|
||||
#define closesocket close
|
||||
#include <netdb.h>
|
||||
#include <netinet/in.h>
|
||||
/* defining MINIUPNPC_IGNORE_EINTR enable the ignore of interruptions
|
||||
* during the connect() call */
|
||||
#define MINIUPNPC_IGNORE_EINTR
|
||||
#include <sys/socket.h>
|
||||
#include <sys/select.h>
|
||||
#endif /* #else _WIN32 */
|
||||
|
||||
#if defined(__amigaos__) || defined(__amigaos4__)
|
||||
#define herror(A) printf("%s\n", A)
|
||||
#endif
|
||||
|
||||
#include "connecthostport.h"
|
||||
|
||||
#ifndef MAXHOSTNAMELEN
|
||||
#define MAXHOSTNAMELEN 64
|
||||
#endif
|
||||
|
||||
/* connecthostport()
|
||||
* return a socket connected (TCP) to the host and port
|
||||
* or -1 in case of error */
|
||||
SOCKET connecthostport(const char * host, unsigned short port,
|
||||
unsigned int scope_id)
|
||||
{
|
||||
SOCKET s;
|
||||
int n;
|
||||
#ifdef USE_GETHOSTBYNAME
|
||||
struct sockaddr_in dest;
|
||||
struct hostent *hp;
|
||||
#else /* #ifdef USE_GETHOSTBYNAME */
|
||||
char tmp_host[MAXHOSTNAMELEN+1];
|
||||
char port_str[8];
|
||||
struct addrinfo *ai, *p;
|
||||
struct addrinfo hints;
|
||||
#endif /* #ifdef USE_GETHOSTBYNAME */
|
||||
#ifdef MINIUPNPC_SET_SOCKET_TIMEOUT
|
||||
struct timeval timeout;
|
||||
#endif /* #ifdef MINIUPNPC_SET_SOCKET_TIMEOUT */
|
||||
|
||||
#ifdef USE_GETHOSTBYNAME
|
||||
hp = gethostbyname(host);
|
||||
if(hp == NULL)
|
||||
{
|
||||
herror(host);
|
||||
return INVALID_SOCKET;
|
||||
}
|
||||
memcpy(&dest.sin_addr, hp->h_addr, sizeof(dest.sin_addr));
|
||||
memset(dest.sin_zero, 0, sizeof(dest.sin_zero));
|
||||
s = socket(PF_INET, SOCK_STREAM, 0);
|
||||
if(ISINVALID(s))
|
||||
{
|
||||
PRINT_SOCKET_ERROR("socket");
|
||||
return INVALID_SOCKET;
|
||||
}
|
||||
#ifdef MINIUPNPC_SET_SOCKET_TIMEOUT
|
||||
/* setting a 3 seconds timeout for the connect() call */
|
||||
timeout.tv_sec = 3;
|
||||
timeout.tv_usec = 0;
|
||||
if(setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(struct timeval)) < 0)
|
||||
{
|
||||
PRINT_SOCKET_ERROR("setsockopt SO_RCVTIMEO");
|
||||
}
|
||||
timeout.tv_sec = 3;
|
||||
timeout.tv_usec = 0;
|
||||
if(setsockopt(s, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(struct timeval)) < 0)
|
||||
{
|
||||
PRINT_SOCKET_ERROR("setsockopt SO_SNDTIMEO");
|
||||
}
|
||||
#endif /* #ifdef MINIUPNPC_SET_SOCKET_TIMEOUT */
|
||||
dest.sin_family = AF_INET;
|
||||
dest.sin_port = htons(port);
|
||||
n = connect(s, (struct sockaddr *)&dest, sizeof(struct sockaddr_in));
|
||||
#ifdef MINIUPNPC_IGNORE_EINTR
|
||||
/* EINTR The system call was interrupted by a signal that was caught
|
||||
* EINPROGRESS The socket is nonblocking and the connection cannot
|
||||
* be completed immediately. */
|
||||
while(n < 0 && (errno == EINTR || errno == EINPROGRESS))
|
||||
{
|
||||
socklen_t len;
|
||||
fd_set wset;
|
||||
int err;
|
||||
FD_ZERO(&wset);
|
||||
FD_SET(s, &wset);
|
||||
#ifdef MINIUPNPC_SET_SOCKET_TIMEOUT
|
||||
timeout.tv_sec = 3;
|
||||
timeout.tv_usec = 0;
|
||||
n = select(s + 1, NULL, &wset, NULL, &timeout);
|
||||
#else
|
||||
n = select(s + 1, NULL, &wset, NULL, NULL);
|
||||
#endif
|
||||
if(n < 0) {
|
||||
if (errno == EINTR)
|
||||
continue; /* try again */
|
||||
else
|
||||
break; /* EBADF, EFAULT, EINVAL */
|
||||
}
|
||||
#ifdef MINIUPNPC_SET_SOCKET_TIMEOUT
|
||||
if(n == 0) {
|
||||
errno = ETIMEDOUT;
|
||||
n = -1;
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
len = sizeof(err);
|
||||
if(getsockopt(s, SOL_SOCKET, SO_ERROR, &err, &len) < 0) {
|
||||
PRINT_SOCKET_ERROR("getsockopt");
|
||||
closesocket(s);
|
||||
return INVALID_SOCKET;
|
||||
}
|
||||
if(err != 0) {
|
||||
errno = err;
|
||||
n = -1;
|
||||
}
|
||||
}
|
||||
#endif /* #ifdef MINIUPNPC_IGNORE_EINTR */
|
||||
if(n<0)
|
||||
{
|
||||
PRINT_SOCKET_ERROR("connect");
|
||||
closesocket(s);
|
||||
return INVALID_SOCKET;
|
||||
}
|
||||
#else /* #ifdef USE_GETHOSTBYNAME */
|
||||
/* use getaddrinfo() instead of gethostbyname() */
|
||||
memset(&hints, 0, sizeof(hints));
|
||||
/* hints.ai_flags = AI_ADDRCONFIG; */
|
||||
#ifdef AI_NUMERICSERV
|
||||
hints.ai_flags = AI_NUMERICSERV;
|
||||
#endif
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
hints.ai_family = AF_UNSPEC; /* AF_INET, AF_INET6 or AF_UNSPEC */
|
||||
/* hints.ai_protocol = IPPROTO_TCP; */
|
||||
snprintf(port_str, sizeof(port_str), "%hu", port);
|
||||
if(host[0] == '[')
|
||||
{
|
||||
/* literal ip v6 address */
|
||||
int i, j;
|
||||
for(i = 0, j = 1; host[j] && (host[j] != ']') && i < MAXHOSTNAMELEN; i++, j++)
|
||||
{
|
||||
tmp_host[i] = host[j];
|
||||
if(0 == strncmp(host+j, "%25", 3)) /* %25 is just url encoding for '%' */
|
||||
j+=2; /* skip "25" */
|
||||
}
|
||||
tmp_host[i] = '\0';
|
||||
}
|
||||
else
|
||||
{
|
||||
strncpy(tmp_host, host, MAXHOSTNAMELEN);
|
||||
}
|
||||
tmp_host[MAXHOSTNAMELEN] = '\0';
|
||||
n = getaddrinfo(tmp_host, port_str, &hints, &ai);
|
||||
if(n != 0)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
fprintf(stderr, "getaddrinfo() error : %d\n", n);
|
||||
#else
|
||||
fprintf(stderr, "getaddrinfo() error : %s\n", gai_strerror(n));
|
||||
#endif
|
||||
return INVALID_SOCKET;
|
||||
}
|
||||
s = INVALID_SOCKET;
|
||||
for(p = ai; p; p = p->ai_next)
|
||||
{
|
||||
if(!ISINVALID(s))
|
||||
closesocket(s);
|
||||
#ifdef DEBUG
|
||||
printf("ai_family=%d ai_socktype=%d ai_protocol=%d (PF_INET=%d, PF_INET6=%d)\n",
|
||||
p->ai_family, p->ai_socktype, p->ai_protocol, PF_INET, PF_INET6);
|
||||
#endif
|
||||
s = socket(p->ai_family, p->ai_socktype, p->ai_protocol);
|
||||
if(ISINVALID(s))
|
||||
continue;
|
||||
if(p->ai_addr->sa_family == AF_INET6 && scope_id > 0) {
|
||||
struct sockaddr_in6 * addr6 = (struct sockaddr_in6 *)p->ai_addr;
|
||||
addr6->sin6_scope_id = scope_id;
|
||||
}
|
||||
#ifdef MINIUPNPC_SET_SOCKET_TIMEOUT
|
||||
/* setting a 3 seconds timeout for the connect() call */
|
||||
timeout.tv_sec = 3;
|
||||
timeout.tv_usec = 0;
|
||||
if(setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(struct timeval)) < 0)
|
||||
{
|
||||
PRINT_SOCKET_ERROR("setsockopt");
|
||||
}
|
||||
timeout.tv_sec = 3;
|
||||
timeout.tv_usec = 0;
|
||||
if(setsockopt(s, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(struct timeval)) < 0)
|
||||
{
|
||||
PRINT_SOCKET_ERROR("setsockopt");
|
||||
}
|
||||
#endif /* #ifdef MINIUPNPC_SET_SOCKET_TIMEOUT */
|
||||
n = connect(s, p->ai_addr, MSC_CAST_INT p->ai_addrlen);
|
||||
#ifdef MINIUPNPC_IGNORE_EINTR
|
||||
/* EINTR The system call was interrupted by a signal that was caught
|
||||
* EINPROGRESS The socket is nonblocking and the connection cannot
|
||||
* be completed immediately. */
|
||||
while(n < 0 && (errno == EINTR || errno == EINPROGRESS))
|
||||
{
|
||||
socklen_t len;
|
||||
fd_set wset;
|
||||
int err;
|
||||
FD_ZERO(&wset);
|
||||
FD_SET(s, &wset);
|
||||
#ifdef MINIUPNPC_SET_SOCKET_TIMEOUT
|
||||
timeout.tv_sec = 3;
|
||||
timeout.tv_usec = 0;
|
||||
n = select(s + 1, NULL, &wset, NULL, &timeout);
|
||||
#else
|
||||
n = select(s + 1, NULL, &wset, NULL, NULL);
|
||||
#endif
|
||||
if(n < 0) {
|
||||
if (errno == EINTR)
|
||||
continue; /* try again */
|
||||
else
|
||||
break; /* EBADF, EFAULT, EINVAL */
|
||||
}
|
||||
#ifdef MINIUPNPC_SET_SOCKET_TIMEOUT
|
||||
if(n == 0) {
|
||||
errno = ETIMEDOUT;
|
||||
n = -1;
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
len = sizeof(err);
|
||||
if(getsockopt(s, SOL_SOCKET, SO_ERROR, &err, &len) < 0) {
|
||||
PRINT_SOCKET_ERROR("getsockopt");
|
||||
closesocket(s);
|
||||
freeaddrinfo(ai);
|
||||
return INVALID_SOCKET;
|
||||
}
|
||||
if(err != 0) {
|
||||
errno = err;
|
||||
n = -1;
|
||||
}
|
||||
}
|
||||
#endif /* #ifdef MINIUPNPC_IGNORE_EINTR */
|
||||
if(n >= 0) /* connect() was successful */
|
||||
break;
|
||||
}
|
||||
freeaddrinfo(ai);
|
||||
if(ISINVALID(s))
|
||||
{
|
||||
PRINT_SOCKET_ERROR("socket");
|
||||
return INVALID_SOCKET;
|
||||
}
|
||||
if(n < 0)
|
||||
{
|
||||
PRINT_SOCKET_ERROR("connect");
|
||||
closesocket(s);
|
||||
return INVALID_SOCKET;
|
||||
}
|
||||
#endif /* #ifdef USE_GETHOSTBYNAME */
|
||||
return s;
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
/* $Id: connecthostport.h,v 1.4 2018/04/06 10:53:13 nanard Exp $ */
|
||||
/* Project: miniupnp
|
||||
* http://miniupnp.free.fr/
|
||||
* Author: Thomas Bernard
|
||||
* Copyright (c) 2010-2018 Thomas Bernard
|
||||
* This software is subjects to the conditions detailed
|
||||
* in the LICENCE file provided within this distribution */
|
||||
#ifndef CONNECTHOSTPORT_H_INCLUDED
|
||||
#define CONNECTHOSTPORT_H_INCLUDED
|
||||
|
||||
#include "miniupnpc_socketdef.h"
|
||||
|
||||
/* connecthostport()
|
||||
* return a socket connected (TCP) to the host and port
|
||||
* or INVALID_SOCKET in case of error */
|
||||
SOCKET connecthostport(const char * host, unsigned short port,
|
||||
unsigned int scope_id);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
#!/bin/sh
|
||||
# $Id: external-ip.sh,v 1.2 2017/11/02 15:33:09 nanard Exp $
|
||||
# (c) 2010 Reuben Hawkins
|
||||
upnpc -s | sed -n -e 's/^ExternalIPAddress = \([0-9.]*\)$/\1/p'
|
||||
@@ -0,0 +1,112 @@
|
||||
' VBScript to generate miniupnpcstrings.h
|
||||
' Copyright 2018 Thomas Bernard
|
||||
'Set WshShell = CreateObject("WScript.Shell")
|
||||
Set FSO = CreateObject("Scripting.FileSystemObject")
|
||||
versionfile = "..\version"
|
||||
infile = "..\miniupnpcstrings.h.in"
|
||||
outfile = "..\miniupnpcstrings.h"
|
||||
outfilerc = "..\rc_version.h"
|
||||
|
||||
On Error Resume Next
|
||||
|
||||
'Wscript.Echo revision
|
||||
|
||||
Err.Clear
|
||||
Set f = FSO.OpenTextFile(versionfile, 1, False) ' 1 = Read
|
||||
If Err.Number = 0 Then
|
||||
version = f.ReadLine
|
||||
f.Close
|
||||
Else
|
||||
' Exit error
|
||||
WScript.Quit 1
|
||||
End If
|
||||
|
||||
os_version = "0.0.0"
|
||||
strComputer = "."
|
||||
Set objWMIService = GetObject("winmgmts:" & "{impersonationLevel=impersonate}!\\" & strComputer & "\root\cimv2")
|
||||
Set colOperatingSystems = objWMIService.ExecQuery ("Select * from Win32_OperatingSystem")
|
||||
For Each objOperatingSystem in colOperatingSystems
|
||||
'Wscript.Echo objOperatingSystem.Caption & " -- "
|
||||
os_version = objOperatingSystem.Version
|
||||
Next
|
||||
|
||||
'Wscript.Echo os_version
|
||||
|
||||
Dim array
|
||||
needWrite = True
|
||||
|
||||
' First Check if the file already contains the right versions
|
||||
Err.Clear
|
||||
Set f_in = FSO.OpenTextFile(outfile, 1, False)
|
||||
If Err.Number = 0 Then
|
||||
old_version = ""
|
||||
old_os_version = ""
|
||||
Do Until f_in.AtEndOfStream
|
||||
line = f_in.ReadLine
|
||||
If Len(line) > 0 Then
|
||||
array = Split(line, " ")
|
||||
If UBound(array) >= 2 And array(0) = "#define" Then
|
||||
If array(1) = "OS_STRING" Then
|
||||
old_os_version = Replace(array(2), Chr(34), "")
|
||||
ElseIf array(1) = "MINIUPNPC_VERSION_STRING" Then
|
||||
old_version = Replace(array(2), Chr(34), "")
|
||||
End if
|
||||
End if
|
||||
End If
|
||||
Loop
|
||||
f_in.Close
|
||||
If old_version = version And old_os_version = "MSWindows/" & os_version Then
|
||||
needWrite = False
|
||||
Else
|
||||
needWrite = True
|
||||
End If
|
||||
End If
|
||||
|
||||
If Not needWrite Then
|
||||
' check files dates
|
||||
Set fIn1 = FSO.GetFile(versionfile)
|
||||
Set fIn2 = FSO.GetFile(infile)
|
||||
Set fOut = FSO.GetFile(outfile)
|
||||
If DateDiff("s", fIn1.DateLastModified, fOut.DateLastModified) < 0 Then
|
||||
needWrite = True
|
||||
End If
|
||||
If DateDiff("s", fIn2.DateLastModified, fOut.DateLastModified) < 0 Then
|
||||
needWrite = True
|
||||
End If
|
||||
End If
|
||||
|
||||
If Not needWrite Then
|
||||
' nothing to do
|
||||
WScript.Quit 0
|
||||
End if
|
||||
|
||||
' generate the file
|
||||
Err.Clear
|
||||
Set f_in = FSO.OpenTextFile(infile, 1, False)
|
||||
If Err.Number = 0 Then
|
||||
Set f_out = FSO.OpenTextFile(outfile, 2, True) ' 2 = Write
|
||||
Do Until f_in.AtEndOfStream
|
||||
line = f_in.ReadLine
|
||||
If Len(line) > 0 Then
|
||||
array = Split(line, " ")
|
||||
If UBound(array) >= 2 And array(0) = "#define" Then
|
||||
If array(1) = "OS_STRING" Then
|
||||
line = "#define OS_STRING " & Chr(34) & "MSWindows/" & os_version & Chr(34)
|
||||
ElseIf array(1) = "MINIUPNPC_VERSION_STRING" Then
|
||||
line = "#define MINIUPNPC_VERSION_STRING " & Chr(34) & version & Chr(34)
|
||||
End if
|
||||
End if
|
||||
End If
|
||||
f_out.WriteLine line
|
||||
Loop
|
||||
f_in.Close
|
||||
f_out.Close
|
||||
End If
|
||||
|
||||
Set f_out = FSO.OpenTextFile(outfilerc, 2, True) ' 2 = Write
|
||||
f_out.WriteLine "#define LIBMINIUPNPC_DOTTED_VERSION " & Chr(34) & version & Chr(34)
|
||||
ver = Split(version, ".")
|
||||
f_out.WriteLine "#define LIBMINIUPNPC_MAJOR_VERSION " & ver(0)
|
||||
f_out.WriteLine "#define LIBMINIUPNPC_MINOR_VERSION " & ver(1)
|
||||
f_out.WriteLine "#define LIBMINIUPNPC_MICRO_VERSION " & ver(2)
|
||||
f_out.Close
|
||||
@@ -0,0 +1,123 @@
|
||||
/* $Id: igd_desc_parse.c,v 1.17 2015/09/15 13:30:04 nanard Exp $ */
|
||||
/* Project : miniupnp
|
||||
* http://miniupnp.free.fr/
|
||||
* Author : Thomas Bernard
|
||||
* Copyright (c) 2005-2015 Thomas Bernard
|
||||
* This software is subject to the conditions detailed in the
|
||||
* LICENCE file provided in this distribution. */
|
||||
|
||||
#include "igd_desc_parse.h"
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
/* Start element handler :
|
||||
* update nesting level counter and copy element name */
|
||||
void IGDstartelt(void * d, const char * name, int l)
|
||||
{
|
||||
struct IGDdatas * datas = (struct IGDdatas *)d;
|
||||
if(l >= MINIUPNPC_URL_MAXSIZE)
|
||||
l = MINIUPNPC_URL_MAXSIZE-1;
|
||||
memcpy(datas->cureltname, name, l);
|
||||
datas->cureltname[l] = '\0';
|
||||
datas->level++;
|
||||
if( (l==7) && !memcmp(name, "service", l) ) {
|
||||
datas->tmp.controlurl[0] = '\0';
|
||||
datas->tmp.eventsuburl[0] = '\0';
|
||||
datas->tmp.scpdurl[0] = '\0';
|
||||
datas->tmp.servicetype[0] = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
#define COMPARE(str, cstr) (0==memcmp(str, cstr, sizeof(cstr) - 1))
|
||||
|
||||
/* End element handler :
|
||||
* update nesting level counter and update parser state if
|
||||
* service element is parsed */
|
||||
void IGDendelt(void * d, const char * name, int l)
|
||||
{
|
||||
struct IGDdatas * datas = (struct IGDdatas *)d;
|
||||
datas->level--;
|
||||
/*printf("endelt %2d %.*s\n", datas->level, l, name);*/
|
||||
if( (l==7) && !memcmp(name, "service", l) )
|
||||
{
|
||||
if(COMPARE(datas->tmp.servicetype,
|
||||
"urn:schemas-upnp-org:service:WANCommonInterfaceConfig:")) {
|
||||
memcpy(&datas->CIF, &datas->tmp, sizeof(struct IGDdatas_service));
|
||||
} else if(COMPARE(datas->tmp.servicetype,
|
||||
"urn:schemas-upnp-org:service:WANIPv6FirewallControl:")) {
|
||||
memcpy(&datas->IPv6FC, &datas->tmp, sizeof(struct IGDdatas_service));
|
||||
} else if(COMPARE(datas->tmp.servicetype,
|
||||
"urn:schemas-upnp-org:service:WANIPConnection:")
|
||||
|| COMPARE(datas->tmp.servicetype,
|
||||
"urn:schemas-upnp-org:service:WANPPPConnection:") ) {
|
||||
if(datas->first.servicetype[0] == '\0') {
|
||||
memcpy(&datas->first, &datas->tmp, sizeof(struct IGDdatas_service));
|
||||
} else {
|
||||
memcpy(&datas->second, &datas->tmp, sizeof(struct IGDdatas_service));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Data handler :
|
||||
* copy data depending on the current element name and state */
|
||||
void IGDdata(void * d, const char * data, int l)
|
||||
{
|
||||
struct IGDdatas * datas = (struct IGDdatas *)d;
|
||||
char * dstmember = 0;
|
||||
/*printf("%2d %s : %.*s\n",
|
||||
datas->level, datas->cureltname, l, data); */
|
||||
if( !strcmp(datas->cureltname, "URLBase") )
|
||||
dstmember = datas->urlbase;
|
||||
else if( !strcmp(datas->cureltname, "presentationURL") )
|
||||
dstmember = datas->presentationurl;
|
||||
else if( !strcmp(datas->cureltname, "serviceType") )
|
||||
dstmember = datas->tmp.servicetype;
|
||||
else if( !strcmp(datas->cureltname, "controlURL") )
|
||||
dstmember = datas->tmp.controlurl;
|
||||
else if( !strcmp(datas->cureltname, "eventSubURL") )
|
||||
dstmember = datas->tmp.eventsuburl;
|
||||
else if( !strcmp(datas->cureltname, "SCPDURL") )
|
||||
dstmember = datas->tmp.scpdurl;
|
||||
/* else if( !strcmp(datas->cureltname, "deviceType") )
|
||||
dstmember = datas->devicetype_tmp;*/
|
||||
if(dstmember)
|
||||
{
|
||||
if(l>=MINIUPNPC_URL_MAXSIZE)
|
||||
l = MINIUPNPC_URL_MAXSIZE-1;
|
||||
memcpy(dstmember, data, l);
|
||||
dstmember[l] = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
void printIGD(struct IGDdatas * d)
|
||||
{
|
||||
printf("urlbase = '%s'\n", d->urlbase);
|
||||
printf("WAN Device (Common interface config) :\n");
|
||||
/*printf(" deviceType = '%s'\n", d->CIF.devicetype);*/
|
||||
printf(" serviceType = '%s'\n", d->CIF.servicetype);
|
||||
printf(" controlURL = '%s'\n", d->CIF.controlurl);
|
||||
printf(" eventSubURL = '%s'\n", d->CIF.eventsuburl);
|
||||
printf(" SCPDURL = '%s'\n", d->CIF.scpdurl);
|
||||
printf("primary WAN Connection Device (IP or PPP Connection):\n");
|
||||
/*printf(" deviceType = '%s'\n", d->first.devicetype);*/
|
||||
printf(" servicetype = '%s'\n", d->first.servicetype);
|
||||
printf(" controlURL = '%s'\n", d->first.controlurl);
|
||||
printf(" eventSubURL = '%s'\n", d->first.eventsuburl);
|
||||
printf(" SCPDURL = '%s'\n", d->first.scpdurl);
|
||||
printf("secondary WAN Connection Device (IP or PPP Connection):\n");
|
||||
/*printf(" deviceType = '%s'\n", d->second.devicetype);*/
|
||||
printf(" servicetype = '%s'\n", d->second.servicetype);
|
||||
printf(" controlURL = '%s'\n", d->second.controlurl);
|
||||
printf(" eventSubURL = '%s'\n", d->second.eventsuburl);
|
||||
printf(" SCPDURL = '%s'\n", d->second.scpdurl);
|
||||
printf("WAN IPv6 Firewall Control :\n");
|
||||
/*printf(" deviceType = '%s'\n", d->IPv6FC.devicetype);*/
|
||||
printf(" servicetype = '%s'\n", d->IPv6FC.servicetype);
|
||||
printf(" controlURL = '%s'\n", d->IPv6FC.controlurl);
|
||||
printf(" eventSubURL = '%s'\n", d->IPv6FC.eventsuburl);
|
||||
printf(" SCPDURL = '%s'\n", d->IPv6FC.scpdurl);
|
||||
}
|
||||
#endif /* DEBUG */
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
/* $Id: igd_desc_parse.h,v 1.14 2025/02/08 23:15:16 nanard Exp $ */
|
||||
/* Project : miniupnp
|
||||
* http://miniupnp.free.fr/ or https://miniupnp.tuxfamily.org/
|
||||
* Author : Thomas Bernard
|
||||
* Copyright (c) 2005-2025 Thomas Bernard
|
||||
* This software is subject to the conditions detailed in the
|
||||
* LICENCE file provided in this distribution.
|
||||
* */
|
||||
#ifndef IGD_DESC_PARSE_H_INCLUDED
|
||||
#define IGD_DESC_PARSE_H_INCLUDED
|
||||
|
||||
/*! \file igd_desc_parse.h
|
||||
* \brief API to parse UPNP device description XML
|
||||
* \todo should not be exposed in the public API
|
||||
*/
|
||||
|
||||
/*! \brief maximum lenght of URLs */
|
||||
#define MINIUPNPC_URL_MAXSIZE (128)
|
||||
|
||||
/*! \brief Structure to store the result of the parsing of UPnP
|
||||
* descriptions of Internet Gateway Devices services */
|
||||
struct IGDdatas_service {
|
||||
/*! \brief controlURL for the service */
|
||||
char controlurl[MINIUPNPC_URL_MAXSIZE];
|
||||
/*! \brief eventSubURL for the service */
|
||||
char eventsuburl[MINIUPNPC_URL_MAXSIZE];
|
||||
/*! \brief SCPDURL for the service */
|
||||
char scpdurl[MINIUPNPC_URL_MAXSIZE];
|
||||
/*! \brief serviceType */
|
||||
char servicetype[MINIUPNPC_URL_MAXSIZE];
|
||||
/*char devicetype[MINIUPNPC_URL_MAXSIZE];*/
|
||||
};
|
||||
|
||||
/*! \brief Structure to store the result of the parsing of UPnP
|
||||
* descriptions of Internet Gateway Devices */
|
||||
struct IGDdatas {
|
||||
/*! \brief current element name */
|
||||
char cureltname[MINIUPNPC_URL_MAXSIZE];
|
||||
/*! \brief URLBase */
|
||||
char urlbase[MINIUPNPC_URL_MAXSIZE];
|
||||
/*! \brief presentationURL */
|
||||
char presentationurl[MINIUPNPC_URL_MAXSIZE];
|
||||
/*! \brief depth into the XML tree */
|
||||
int level;
|
||||
/*! \brief "urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1" */
|
||||
struct IGDdatas_service CIF;
|
||||
/*! \brief first of "urn:schemas-upnp-org:service:WANIPConnection:1"
|
||||
* or "urn:schemas-upnp-org:service:WANPPPConnection:1" */
|
||||
struct IGDdatas_service first;
|
||||
/*! \brief second of "urn:schemas-upnp-org:service:WANIPConnection:1"
|
||||
* or "urn:schemas-upnp-org:service:WANPPPConnection:1" */
|
||||
struct IGDdatas_service second;
|
||||
/*! \brief "urn:schemas-upnp-org:service:WANIPv6FirewallControl:1" */
|
||||
struct IGDdatas_service IPv6FC;
|
||||
/*! \brief currently parsed service */
|
||||
struct IGDdatas_service tmp;
|
||||
};
|
||||
|
||||
/*!
|
||||
* \brief XML start element handler
|
||||
*/
|
||||
void IGDstartelt(void *, const char *, int);
|
||||
/*!
|
||||
* \brief XML end element handler
|
||||
*/
|
||||
void IGDendelt(void *, const char *, int);
|
||||
/*!
|
||||
* \brief XML characted data handler
|
||||
*/
|
||||
void IGDdata(void *, const char *, int);
|
||||
#ifdef DEBUG
|
||||
void printIGD(struct IGDdatas *);
|
||||
#endif /* DEBUG */
|
||||
|
||||
#endif /* IGD_DESC_PARSE_H_INCLUDED */
|
||||
@@ -0,0 +1,98 @@
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.IntBuffer;
|
||||
|
||||
import fr.free.miniupnp.*;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author syuu
|
||||
*/
|
||||
public class JavaBridgeTest {
|
||||
public static void main(String[] args) {
|
||||
int UPNP_DELAY = 2000;
|
||||
MiniupnpcLibrary miniupnpc = MiniupnpcLibrary.INSTANCE;
|
||||
UPNPDev devlist = null;
|
||||
UPNPUrls urls = new UPNPUrls();
|
||||
IGDdatas data = new IGDdatas();
|
||||
ByteBuffer lanaddr = ByteBuffer.allocate(16);
|
||||
ByteBuffer wanaddr = ByteBuffer.allocate(16);
|
||||
ByteBuffer intClient = ByteBuffer.allocate(16);
|
||||
ByteBuffer intPort = ByteBuffer.allocate(6);
|
||||
ByteBuffer desc = ByteBuffer.allocate(80);
|
||||
ByteBuffer enabled = ByteBuffer.allocate(4);
|
||||
ByteBuffer leaseDuration = ByteBuffer.allocate(16);
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
if(args.length < 2) {
|
||||
System.err.println("Usage : java [...] JavaBridgeTest port protocol");
|
||||
System.out.println(" port is numeric, protocol is TCP or UDP");
|
||||
return;
|
||||
}
|
||||
|
||||
devlist = miniupnpc.upnpDiscover(UPNP_DELAY, (String) null, (String) null, 0, 0, (byte)2, IntBuffer.allocate(1));
|
||||
if (devlist != null) {
|
||||
System.out.println("List of UPNP devices found on the network :");
|
||||
for (UPNPDev device = devlist; device != null; device = device.pNext) {
|
||||
System.out.println("desc: " + device.descURL.getString(0) + " st: " + device.st.getString(0));
|
||||
}
|
||||
if ((i = miniupnpc.UPNP_GetValidIGD(devlist, urls, data, lanaddr, 16, wanaddr, 16)) != 0) {
|
||||
switch (i) {
|
||||
case 1:
|
||||
System.out.println("Found valid IGD : " + urls.controlURL.getString(0));
|
||||
break;
|
||||
case 2:
|
||||
System.out.println("Found a (not connected?) IGD : " + urls.controlURL.getString(0));
|
||||
System.out.println("Trying to continue anyway");
|
||||
break;
|
||||
case 3:
|
||||
System.out.println("UPnP device found. Is it an IGD ? : " + urls.controlURL.getString(0));
|
||||
System.out.println("Trying to continue anyway");
|
||||
break;
|
||||
default:
|
||||
System.out.println("Found device (igd ?) : " + urls.controlURL.getString(0));
|
||||
System.out.println("Trying to continue anyway");
|
||||
|
||||
}
|
||||
System.out.println("Local LAN ip address : " + new String(lanaddr.array()));
|
||||
ByteBuffer externalAddress = ByteBuffer.allocate(16);
|
||||
miniupnpc.UPNP_GetExternalIPAddress(urls.controlURL.getString(0),
|
||||
new String(data.first.servicetype), externalAddress);
|
||||
System.out.println("ExternalIPAddress = " + new String(externalAddress.array()));
|
||||
ret = miniupnpc.UPNP_AddPortMapping(
|
||||
urls.controlURL.getString(0), // controlURL
|
||||
new String(data.first.servicetype), // servicetype
|
||||
args[0], // external Port
|
||||
args[0], // internal Port
|
||||
new String(lanaddr.array()), // internal client
|
||||
"added via miniupnpc/JAVA !", // description
|
||||
args[1], // protocol UDP or TCP
|
||||
null, // remote host (useless)
|
||||
"0"); // leaseDuration
|
||||
if (ret != MiniupnpcLibrary.UPNPCOMMAND_SUCCESS)
|
||||
System.out.println("AddPortMapping() failed with code " + ret);
|
||||
ret = miniupnpc.UPNP_GetSpecificPortMappingEntry(
|
||||
urls.controlURL.getString(0), new String(data.first.servicetype),
|
||||
args[0], args[1], null, intClient, intPort,
|
||||
desc, enabled, leaseDuration);
|
||||
if (ret != MiniupnpcLibrary.UPNPCOMMAND_SUCCESS)
|
||||
System.out.println("GetSpecificPortMappingEntry() failed with code " + ret);
|
||||
System.out.println("InternalIP:Port = " +
|
||||
new String(intClient.array()) + ":" + new String(intPort.array()) +
|
||||
" (" + new String(desc.array()) + ")");
|
||||
ret = miniupnpc.UPNP_DeletePortMapping(
|
||||
urls.controlURL.getString(0),
|
||||
new String(data.first.servicetype),
|
||||
args[0], args[1], null);
|
||||
if (ret != MiniupnpcLibrary.UPNPCOMMAND_SUCCESS)
|
||||
System.out.println("DelPortMapping() failed with code " + ret);
|
||||
miniupnpc.FreeUPNPUrls(urls);
|
||||
} else {
|
||||
System.out.println("No valid UPNP Internet Gateway Device found.");
|
||||
}
|
||||
miniupnpc.freeUPNPDevlist(devlist);
|
||||
} else {
|
||||
System.out.println("No IGD UPnP Device found on the network !\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
@echo off
|
||||
set JAVA=java
|
||||
set JAVAC=javac
|
||||
REM notice the semicolon for Windows. Write once, run ... oh nevermind
|
||||
set CP=miniupnpc_win32.jar;.
|
||||
|
||||
%JAVAC% -cp "%CP%" JavaBridgeTest.java || exit 1
|
||||
%JAVA% -cp "%CP%" JavaBridgeTest 12345 UDP || exit 1
|
||||
@@ -0,0 +1,8 @@
|
||||
#!/bin/bash
|
||||
|
||||
JAVA=java
|
||||
JAVAC=javac
|
||||
CP=$(for i in *.jar; do echo -n $i:; done).
|
||||
|
||||
$JAVAC -cp $CP JavaBridgeTest.java || exit 1
|
||||
$JAVA -cp $CP JavaBridgeTest 12345 UDP || exit 1
|
||||
@@ -0,0 +1,198 @@
|
||||
/* $Id: listdevices.c,v 1.9 2024/01/26 23:30:47 nanard Exp $ */
|
||||
/* Project : miniupnp
|
||||
* Author : Thomas Bernard
|
||||
* Copyright (c) 2013-2015 Thomas Bernard
|
||||
* This software is subject to the conditions detailed in the
|
||||
* LICENCE file provided in this distribution. */
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#ifdef _WIN32
|
||||
#include <winsock2.h>
|
||||
#endif /* _WIN32 */
|
||||
#include "miniupnpc.h"
|
||||
|
||||
struct upnp_dev_list {
|
||||
struct upnp_dev_list * next;
|
||||
char * descURL;
|
||||
struct UPNPDev * * array;
|
||||
size_t count;
|
||||
size_t allocated_count;
|
||||
};
|
||||
|
||||
#define ADD_DEVICE_COUNT_STEP 16
|
||||
|
||||
void add_device(struct upnp_dev_list * * list_head, struct UPNPDev * dev)
|
||||
{
|
||||
struct upnp_dev_list * elt;
|
||||
size_t i;
|
||||
|
||||
if(dev == NULL)
|
||||
return;
|
||||
for(elt = *list_head; elt != NULL; elt = elt->next) {
|
||||
if(strcmp(elt->descURL, dev->descURL) == 0) {
|
||||
for(i = 0; i < elt->count; i++) {
|
||||
if (strcmp(elt->array[i]->st, dev->st) == 0 && strcmp(elt->array[i]->usn, dev->usn) == 0) {
|
||||
return; /* already found */
|
||||
}
|
||||
}
|
||||
if(elt->count >= elt->allocated_count) {
|
||||
struct UPNPDev * * tmp;
|
||||
elt->allocated_count += ADD_DEVICE_COUNT_STEP;
|
||||
tmp = realloc(elt->array, elt->allocated_count * sizeof(struct UPNPDev *));
|
||||
if(tmp == NULL) {
|
||||
fprintf(stderr, "Failed to realloc(%p, %lu)\n", elt->array, (unsigned long)(elt->allocated_count * sizeof(struct UPNPDev *)));
|
||||
return;
|
||||
}
|
||||
elt->array = tmp;
|
||||
}
|
||||
elt->array[elt->count++] = dev;
|
||||
return;
|
||||
}
|
||||
}
|
||||
elt = malloc(sizeof(struct upnp_dev_list));
|
||||
if(elt == NULL) {
|
||||
fprintf(stderr, "Failed to malloc(%lu)\n", (unsigned long)sizeof(struct upnp_dev_list));
|
||||
return;
|
||||
}
|
||||
elt->next = *list_head;
|
||||
elt->descURL = strdup(dev->descURL);
|
||||
if(elt->descURL == NULL) {
|
||||
fprintf(stderr, "Failed to strdup(%s)\n", dev->descURL);
|
||||
free(elt);
|
||||
return;
|
||||
}
|
||||
elt->allocated_count = ADD_DEVICE_COUNT_STEP;
|
||||
elt->array = malloc(ADD_DEVICE_COUNT_STEP * sizeof(struct UPNPDev *));
|
||||
if(elt->array == NULL) {
|
||||
fprintf(stderr, "Failed to malloc(%lu)\n", (unsigned long)(ADD_DEVICE_COUNT_STEP * sizeof(struct UPNPDev *)));
|
||||
free(elt->descURL);
|
||||
free(elt);
|
||||
return;
|
||||
}
|
||||
elt->array[0] = dev;
|
||||
elt->count = 1;
|
||||
*list_head = elt;
|
||||
}
|
||||
|
||||
void free_device(struct upnp_dev_list * elt)
|
||||
{
|
||||
free(elt->descURL);
|
||||
free(elt->array);
|
||||
free(elt);
|
||||
}
|
||||
|
||||
int main(int argc, char * * argv)
|
||||
{
|
||||
const char * searched_device = NULL;
|
||||
const char * * searched_devices = NULL;
|
||||
const char * multicastif = 0;
|
||||
const char * minissdpdpath = 0;
|
||||
int ipv6 = 0;
|
||||
unsigned char ttl = 2;
|
||||
int error = 0;
|
||||
struct UPNPDev * devlist = 0;
|
||||
struct UPNPDev * dev;
|
||||
struct upnp_dev_list * sorted_list = NULL;
|
||||
struct upnp_dev_list * dev_array;
|
||||
int i;
|
||||
|
||||
#ifdef _WIN32
|
||||
WSADATA wsaData;
|
||||
int nResult = WSAStartup(MAKEWORD(2,2), &wsaData);
|
||||
if(nResult != NO_ERROR)
|
||||
{
|
||||
fprintf(stderr, "WSAStartup() failed.\n");
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
for(i = 1; i < argc; i++) {
|
||||
if(strcmp(argv[i], "-6") == 0)
|
||||
ipv6 = 1;
|
||||
else if(strcmp(argv[i], "-d") == 0) {
|
||||
if(++i >= argc) {
|
||||
fprintf(stderr, "%s option needs one argument\n", "-d");
|
||||
return 1;
|
||||
}
|
||||
searched_device = argv[i];
|
||||
} else if(strcmp(argv[i], "-t") == 0) {
|
||||
if(++i >= argc) {
|
||||
fprintf(stderr, "%s option needs one argument\n", "-t");
|
||||
return 1;
|
||||
}
|
||||
ttl = (unsigned char)atoi(argv[i]);
|
||||
} else if(strcmp(argv[i], "-l") == 0) {
|
||||
if(++i >= argc) {
|
||||
fprintf(stderr, "-l option needs at least one argument\n");
|
||||
return 1;
|
||||
}
|
||||
searched_devices = (const char * *)(argv + i);
|
||||
break;
|
||||
} else if(strcmp(argv[i], "-m") == 0) {
|
||||
if(++i >= argc) {
|
||||
fprintf(stderr, "-m option needs one argument\n");
|
||||
return 1;
|
||||
}
|
||||
multicastif = argv[i];
|
||||
} else {
|
||||
printf("usage : %s [options] [-l <device1> <device2> ...]\n", argv[0]);
|
||||
printf("options :\n");
|
||||
printf(" -6 : use IPv6\n");
|
||||
printf(" -m address/ifname : network interface to use for multicast\n");
|
||||
printf(" -d <device string> : search only for this type of device\n");
|
||||
printf(" -l <device1> <device2> ... : search only for theses types of device\n");
|
||||
printf(" -t ttl : set multicast TTL. Default value is 2.\n");
|
||||
printf(" -h : this help\n");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
if(searched_device) {
|
||||
printf("searching UPnP device type %s\n", searched_device);
|
||||
devlist = upnpDiscoverDevice(searched_device,
|
||||
2000, multicastif, minissdpdpath,
|
||||
0/*localport*/, ipv6, ttl, &error);
|
||||
} else if(searched_devices) {
|
||||
printf("searching UPnP device types :\n");
|
||||
for(i = 0; searched_devices[i]; i++)
|
||||
printf("\t%s\n", searched_devices[i]);
|
||||
devlist = upnpDiscoverDevices(searched_devices,
|
||||
2000, multicastif, minissdpdpath,
|
||||
0/*localport*/, ipv6, ttl, &error, 1);
|
||||
} else {
|
||||
printf("searching all UPnP devices\n");
|
||||
devlist = upnpDiscoverAll(2000, multicastif, minissdpdpath,
|
||||
0/*localport*/, ipv6, ttl, &error);
|
||||
}
|
||||
if(devlist) {
|
||||
for(dev = devlist, i = 1; dev != NULL; dev = dev->pNext, i++) {
|
||||
printf("%3d: %-48s\n", i, dev->st);
|
||||
printf(" %s\n", dev->descURL);
|
||||
printf(" %s\n", dev->usn);
|
||||
add_device(&sorted_list, dev);
|
||||
}
|
||||
putchar('\n');
|
||||
for (dev_array = sorted_list; dev_array != NULL ; dev_array = dev_array->next) {
|
||||
printf("%s :\n", dev_array->descURL);
|
||||
for(i = 0; (unsigned)i < dev_array->count; i++) {
|
||||
printf("%2d: %s\n", i+1, dev_array->array[i]->st);
|
||||
printf(" %s\n", dev_array->array[i]->usn);
|
||||
}
|
||||
putchar('\n');
|
||||
}
|
||||
freeUPNPDevlist(devlist);
|
||||
while(sorted_list != NULL) {
|
||||
dev_array = sorted_list;
|
||||
sorted_list = sorted_list->next;
|
||||
free_device(dev_array);
|
||||
}
|
||||
} else {
|
||||
fprintf(stderr,"No UPnP devices found\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
.TH MINIUPNPC 3
|
||||
.SH NAME
|
||||
miniupnpc \- UPnP client library
|
||||
.SH SYNOPSIS
|
||||
.SH DESCRIPTION
|
||||
The miniupnpc library implement the UPnP protocol defined
|
||||
to dialog with Internet Gateway Devices. It also has
|
||||
the ability to use data gathered by minissdpd(1) about
|
||||
UPnP devices up on the network in order to skip the
|
||||
long UPnP device discovery process.
|
||||
.PP
|
||||
At first, upnpDiscover(3) has to be used to discover UPnP IGD present
|
||||
on the network. Then UPNP_GetValidIGD(3) to select the right one.
|
||||
Alternatively, UPNP_GetIGDFromUrl(3) could be used to bypass discovery
|
||||
process if the root description url of the device to use is known.
|
||||
Then all the UPNP_* functions can be used, such as
|
||||
UPNP_GetConnectionTypeInfo(3), UPNP_AddPortMapping(3), etc...
|
||||
.SH "HEADER FILES"
|
||||
.IP miniupnpc.h
|
||||
That's the main header file for the miniupnpc library API.
|
||||
It contains all the functions and structures related to device discovery.
|
||||
.IP upnpcommands.h
|
||||
This header file contain the UPnP IGD methods that are accessible
|
||||
through the miniupnpc API. The name of the C functions are matching
|
||||
the UPnP methods names. ie: GetGenericPortMappingEntry is
|
||||
UPNP_GetGenericPortMappingEntry.
|
||||
.SH "API FUNCTIONS"
|
||||
.IP "struct UPNPDev * upnpDiscover(int delay, const char * multicastif, const char * minissdpdsock, int localport, int ipv6, int * error);"
|
||||
execute the discovery process.
|
||||
delay (in millisecond) is the maximum time for waiting any device response.
|
||||
If available, device list will be obtained from MiniSSDPd.
|
||||
Default path for minissdpd socket will be used if minissdpdsock argument is NULL.
|
||||
If multicastif is not NULL, it will be used instead of the default multicast interface for sending SSDP discover packets.
|
||||
If localport is set to UPNP_LOCAL_PORT_SAME(1) SSDP packets will be sent
|
||||
from the source port 1900 (same as destination port), if set to
|
||||
UPNP_LOCAL_PORT_ANY(0) system assign a source port, any other value will
|
||||
be attempted as the source port.
|
||||
If ipv6 is not 0, IPv6 is used instead of IPv4 for the discovery process.
|
||||
.IP "void freeUPNPDevlist(struct UPNPDev * devlist);"
|
||||
free the list returned by upnpDiscover().
|
||||
.IP "int UPNP_GetValidIGD(struct UPNPDev * devlist, struct UPNPUrls * urls, struct IGDdatas * data, char * lanaddr, int lanaddrlen, char * wanaddr, int wanaddrlen);"
|
||||
browse the list of device returned by upnpDiscover(), find
|
||||
a live UPnP internet gateway device and fill structures passed as arguments
|
||||
with data used for UPNP methods invocation.
|
||||
.IP "int UPNP_GetIGDFromUrl(const char * rootdescurl, struct UPNPUrls * urls, struct IGDdatas * data, char * lanaddr, int lanaddrlen);"
|
||||
permit one to bypass the upnpDiscover() call if the xml root description
|
||||
URL of the UPnP IGD is known.
|
||||
Fill structures passed as arguments
|
||||
with data used for UPNP methods invocation.
|
||||
.IP "void GetUPNPUrls(struct UPNPUrls *, struct IGDdatas *, const char *);"
|
||||
.IP "void FreeUPNPUrls(struct UPNPUrls *);"
|
||||
|
||||
.SH "SEE ALSO"
|
||||
minissdpd(1)
|
||||
.SH BUGS
|
||||
@@ -0,0 +1,8 @@
|
||||
@mingw32-make -f Makefile.mingw %1
|
||||
@if errorlevel 1 goto end
|
||||
@if not exist upnpc-static.exe goto end
|
||||
@strip upnpc-static.exe
|
||||
@upx --best upnpc-static.exe
|
||||
@strip upnpc-shared.exe
|
||||
@upx --best upnpc-shared.exe
|
||||
:end
|
||||
@@ -0,0 +1,690 @@
|
||||
/* $Id: minihttptestserver.c,v 1.25 2020/05/29 21:14:22 nanard Exp $ */
|
||||
/* Project : miniUPnP
|
||||
* Author : Thomas Bernard
|
||||
* Copyright (c) 2011-2018 Thomas Bernard
|
||||
* This software is subject to the conditions detailed in the
|
||||
* LICENCE file provided in this distribution.
|
||||
* */
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/wait.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <netinet/in.h>
|
||||
#include <signal.h>
|
||||
#include <time.h>
|
||||
#include <errno.h>
|
||||
|
||||
#ifndef INADDR_LOOPBACK
|
||||
#define INADDR_LOOPBACK 0x7f000001
|
||||
#endif
|
||||
|
||||
#define CRAP_LENGTH (2048)
|
||||
|
||||
static int server(unsigned short port, const char * expected_file_name, int ipv6);
|
||||
|
||||
volatile sig_atomic_t quit = 0;
|
||||
volatile sig_atomic_t child_to_wait_for = 0;
|
||||
|
||||
/**
|
||||
* signal handler for SIGCHLD (child status has changed)
|
||||
*/
|
||||
void handle_signal_chld(int sig)
|
||||
{
|
||||
(void)sig;
|
||||
/* printf("handle_signal_chld(%d)\n", sig); */
|
||||
++child_to_wait_for;
|
||||
}
|
||||
|
||||
/**
|
||||
* signal handler for SIGINT (CRTL C)
|
||||
*/
|
||||
void handle_signal_int(int sig)
|
||||
{
|
||||
(void)sig;
|
||||
/* printf("handle_signal_int(%d)\n", sig); */
|
||||
quit = 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* build a text/plain content of the specified length
|
||||
*/
|
||||
void build_content(char * p, size_t n)
|
||||
{
|
||||
char line_buffer[80];
|
||||
int k;
|
||||
int i = 0;
|
||||
|
||||
while(n > 0) {
|
||||
k = snprintf(line_buffer, sizeof(line_buffer),
|
||||
"%04d_ABCDEFGHIJKL_This_line_is_64_bytes_long_ABCDEFGHIJKL_%04d\r\n",
|
||||
i, i);
|
||||
if(k != 64) {
|
||||
fprintf(stderr, "snprintf() returned %d in build_content()\n", k);
|
||||
}
|
||||
++i;
|
||||
if(n >= 64) {
|
||||
memcpy(p, line_buffer, 64);
|
||||
p += 64;
|
||||
n -= 64;
|
||||
} else {
|
||||
memcpy(p, line_buffer, n);
|
||||
p += n;
|
||||
n = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* build crappy content
|
||||
*/
|
||||
void build_crap(char * p, size_t n)
|
||||
{
|
||||
static const char crap[] = "_CRAP_\r\n";
|
||||
size_t i;
|
||||
|
||||
while(n > 0) {
|
||||
i = sizeof(crap) - 1;
|
||||
if(i > n)
|
||||
i = n;
|
||||
memcpy(p, crap, i);
|
||||
p += i;
|
||||
n -= i;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* build chunked response.
|
||||
* return a malloc'ed buffer
|
||||
*/
|
||||
char * build_chunked_response(size_t content_length, size_t * response_len)
|
||||
{
|
||||
char * response_buffer;
|
||||
char * content_buffer;
|
||||
size_t buffer_length;
|
||||
size_t i;
|
||||
unsigned int n;
|
||||
|
||||
/* allocate to have some margin */
|
||||
buffer_length = 256 + content_length + (content_length >> 4);
|
||||
response_buffer = malloc(buffer_length);
|
||||
if(response_buffer == NULL)
|
||||
return NULL;
|
||||
*response_len = snprintf(response_buffer, buffer_length,
|
||||
"HTTP/1.1 200 OK\r\n"
|
||||
"Content-Type: text/plain\r\n"
|
||||
"Transfer-Encoding: chunked\r\n"
|
||||
"\r\n");
|
||||
|
||||
/* build the content */
|
||||
content_buffer = malloc(content_length);
|
||||
if(content_buffer == NULL) {
|
||||
free(response_buffer);
|
||||
return NULL;
|
||||
}
|
||||
build_content(content_buffer, content_length);
|
||||
|
||||
/* chunk it */
|
||||
i = 0;
|
||||
while(i < content_length) {
|
||||
n = (rand() % 199) + 1;
|
||||
if(i + n > content_length) {
|
||||
n = content_length - i;
|
||||
}
|
||||
/* TODO : check buffer size ! */
|
||||
*response_len += snprintf(response_buffer + *response_len,
|
||||
buffer_length - *response_len,
|
||||
"%x\r\n", n);
|
||||
memcpy(response_buffer + *response_len, content_buffer + i, n);
|
||||
*response_len += n;
|
||||
i += n;
|
||||
response_buffer[(*response_len)++] = '\r';
|
||||
response_buffer[(*response_len)++] = '\n';
|
||||
}
|
||||
/* the last chunk : "0\r\n" a empty body and then
|
||||
* the final "\r\n" */
|
||||
memcpy(response_buffer + *response_len, "0\r\n\r\n", 5);
|
||||
*response_len += 5;
|
||||
free(content_buffer);
|
||||
|
||||
printf("resp_length=%lu buffer_length=%lu content_length=%lu\n",
|
||||
*response_len, buffer_length, content_length);
|
||||
return response_buffer;
|
||||
}
|
||||
|
||||
/* favicon.ico generator */
|
||||
#ifdef OLD_HEADER
|
||||
#define FAVICON_LENGTH (6 + 16 + 12 + 8 + 32 * 4)
|
||||
#else
|
||||
#define FAVICON_LENGTH (6 + 16 + 40 + 8 + 32 * 4)
|
||||
#endif
|
||||
void build_favicon_content(unsigned char * p, size_t n)
|
||||
{
|
||||
int i;
|
||||
if(n < FAVICON_LENGTH)
|
||||
return;
|
||||
/* header : 6 bytes */
|
||||
*p++ = 0;
|
||||
*p++ = 0;
|
||||
*p++ = 1; /* type : ICO */
|
||||
*p++ = 0;
|
||||
*p++ = 1; /* number of images in file */
|
||||
*p++ = 0;
|
||||
/* image directory (1 entry) : 16 bytes */
|
||||
*p++ = 16; /* width */
|
||||
*p++ = 16; /* height */
|
||||
*p++ = 2; /* number of colors in the palette. 0 = no palette */
|
||||
*p++ = 0; /* reserved */
|
||||
*p++ = 1; /* color planes */
|
||||
*p++ = 0; /* " */
|
||||
*p++ = 1; /* bpp */
|
||||
*p++ = 0; /* " */
|
||||
#ifdef OLD_HEADER
|
||||
*p++ = 12 + 8 + 32 * 4; /* bmp size */
|
||||
#else
|
||||
*p++ = 40 + 8 + 32 * 4; /* bmp size */
|
||||
#endif
|
||||
*p++ = 0; /* " */
|
||||
*p++ = 0; /* " */
|
||||
*p++ = 0; /* " */
|
||||
*p++ = 6 + 16; /* bmp offset */
|
||||
*p++ = 0; /* " */
|
||||
*p++ = 0; /* " */
|
||||
*p++ = 0; /* " */
|
||||
/* BMP */
|
||||
#ifdef OLD_HEADER
|
||||
/* BITMAPCOREHEADER */
|
||||
*p++ = 12; /* size of this header */
|
||||
*p++ = 0; /* " */
|
||||
*p++ = 0; /* " */
|
||||
*p++ = 0; /* " */
|
||||
*p++ = 16; /* width */
|
||||
*p++ = 0; /* " */
|
||||
*p++ = 16 * 2; /* height x 2 ! */
|
||||
*p++ = 0; /* " */
|
||||
*p++ = 1; /* color planes */
|
||||
*p++ = 0; /* " */
|
||||
*p++ = 1; /* bpp */
|
||||
*p++ = 0; /* " */
|
||||
#else
|
||||
/* BITMAPINFOHEADER */
|
||||
*p++ = 40; /* size of this header */
|
||||
*p++ = 0; /* " */
|
||||
*p++ = 0; /* " */
|
||||
*p++ = 0; /* " */
|
||||
*p++ = 16; /* width */
|
||||
*p++ = 0; /* " */
|
||||
*p++ = 0; /* " */
|
||||
*p++ = 0; /* " */
|
||||
*p++ = 16 * 2; /* height x 2 ! */
|
||||
*p++ = 0; /* " */
|
||||
*p++ = 0; /* " */
|
||||
*p++ = 0; /* " */
|
||||
*p++ = 1; /* color planes */
|
||||
*p++ = 0; /* " */
|
||||
*p++ = 1; /* bpp */
|
||||
*p++ = 0; /* " */
|
||||
/* compression method, image size, ppm x, ppm y */
|
||||
/* colors in the palette ? */
|
||||
/* important colors */
|
||||
for(i = 4 * 6; i > 0; --i)
|
||||
*p++ = 0;
|
||||
#endif
|
||||
/* palette */
|
||||
*p++ = 0; /* b */
|
||||
*p++ = 0; /* g */
|
||||
*p++ = 0; /* r */
|
||||
*p++ = 0; /* reserved */
|
||||
*p++ = 255; /* b */
|
||||
*p++ = 255; /* g */
|
||||
*p++ = 255; /* r */
|
||||
*p++ = 0; /* reserved */
|
||||
/* pixel data */
|
||||
for(i = 16; i > 0; --i) {
|
||||
if(i & 1) {
|
||||
*p++ = 0125;
|
||||
*p++ = 0125;
|
||||
} else {
|
||||
*p++ = 0252;
|
||||
*p++ = 0252;
|
||||
}
|
||||
*p++ = 0;
|
||||
*p++ = 0;
|
||||
}
|
||||
/* Opacity MASK */
|
||||
for(i = 16 * 4; i > 0; --i) {
|
||||
*p++ = 0;
|
||||
}
|
||||
}
|
||||
|
||||
enum modes {
|
||||
MODE_INVALID, MODE_CHUNKED, MODE_ADDCRAP, MODE_NORMAL, MODE_FAVICON, MODE_MALFORMED
|
||||
};
|
||||
|
||||
const struct {
|
||||
const enum modes mode;
|
||||
const char * text;
|
||||
} modes_array[] = {
|
||||
{MODE_CHUNKED, "chunked"},
|
||||
{MODE_ADDCRAP, "addcrap"},
|
||||
{MODE_NORMAL, "normal"},
|
||||
{MODE_FAVICON, "favicon.ico"},
|
||||
{MODE_MALFORMED, "malformed"},
|
||||
{MODE_INVALID, NULL}
|
||||
};
|
||||
|
||||
/**
|
||||
* write the response with random behaviour !
|
||||
*/
|
||||
void send_response(int c, const char * buffer, size_t len)
|
||||
{
|
||||
ssize_t n;
|
||||
while(len > 0) {
|
||||
n = (rand() % 99) + 1;
|
||||
if((size_t)n > len)
|
||||
n = len;
|
||||
n = write(c, buffer, n);
|
||||
if(n < 0) {
|
||||
if(errno != EINTR) {
|
||||
perror("write");
|
||||
return;
|
||||
}
|
||||
/* if errno == EINTR, try again */
|
||||
} else {
|
||||
len -= n;
|
||||
buffer += n;
|
||||
usleep(10000); /* 10ms */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* handle the HTTP connection
|
||||
*/
|
||||
void handle_http_connection(int c)
|
||||
{
|
||||
char request_buffer[2048];
|
||||
size_t request_len = 0;
|
||||
int headers_found = 0;
|
||||
ssize_t n, m;
|
||||
size_t i;
|
||||
char request_method[16];
|
||||
char request_uri[256];
|
||||
char http_version[16];
|
||||
char * p;
|
||||
char * response_buffer;
|
||||
size_t response_len;
|
||||
enum modes mode;
|
||||
size_t content_length = 16*1024;
|
||||
|
||||
/* read the request */
|
||||
while(request_len < sizeof(request_buffer) && !headers_found) {
|
||||
n = read(c,
|
||||
request_buffer + request_len,
|
||||
sizeof(request_buffer) - request_len);
|
||||
if(n < 0) {
|
||||
if(errno == EINTR)
|
||||
continue;
|
||||
perror("read");
|
||||
return;
|
||||
} else if(n==0) {
|
||||
/* remote host closed the connection */
|
||||
break;
|
||||
} else {
|
||||
request_len += n;
|
||||
for(i = 0; i < request_len - 3; i++) {
|
||||
if(0 == memcmp(request_buffer + i, "\r\n\r\n", 4)) {
|
||||
/* found the end of headers */
|
||||
headers_found = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if(!headers_found) {
|
||||
/* error */
|
||||
printf("no HTTP header found in the request\n");
|
||||
return;
|
||||
}
|
||||
printf("headers :\n%.*s", (int)request_len, request_buffer);
|
||||
/* the request have been received, now parse the request line */
|
||||
p = request_buffer;
|
||||
for(i = 0; i < sizeof(request_method) - 1; i++) {
|
||||
if(*p == ' ' || *p == '\r')
|
||||
break;
|
||||
request_method[i] = *p;
|
||||
++p;
|
||||
}
|
||||
request_method[i] = '\0';
|
||||
while(*p == ' ')
|
||||
p++;
|
||||
for(i = 0; i < (int)sizeof(request_uri) - 1; i++) {
|
||||
if(*p == ' ' || *p == '\r')
|
||||
break;
|
||||
request_uri[i] = *p;
|
||||
++p;
|
||||
}
|
||||
request_uri[i] = '\0';
|
||||
while(*p == ' ')
|
||||
p++;
|
||||
for(i = 0; i < (int)sizeof(http_version) - 1; i++) {
|
||||
if(*p == ' ' || *p == '\r')
|
||||
break;
|
||||
http_version[i] = *p;
|
||||
++p;
|
||||
}
|
||||
http_version[i] = '\0';
|
||||
printf("Method = %s, URI = %s, %s\n",
|
||||
request_method, request_uri, http_version);
|
||||
/* check if the request method is allowed */
|
||||
if(0 != strcmp(request_method, "GET")) {
|
||||
const char response405[] = "HTTP/1.1 405 Method Not Allowed\r\n"
|
||||
"Allow: GET\r\n\r\n";
|
||||
const char * pc;
|
||||
/* 405 Method Not Allowed */
|
||||
/* The response MUST include an Allow header containing a list
|
||||
* of valid methods for the requested resource. */
|
||||
n = sizeof(response405) - 1;
|
||||
pc = response405;
|
||||
while(n > 0) {
|
||||
m = write(c, pc, n);
|
||||
if(m<0) {
|
||||
if(errno != EINTR) {
|
||||
perror("write");
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
n -= m;
|
||||
pc += m;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
mode = MODE_INVALID;
|
||||
/* use the request URI to know what to do */
|
||||
for(i = 0; modes_array[i].mode != MODE_INVALID; i++) {
|
||||
if(strstr(request_uri, modes_array[i].text)) {
|
||||
mode = modes_array[i].mode; /* found */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
switch(mode) {
|
||||
case MODE_MALFORMED:
|
||||
response_len = 2048;
|
||||
response_buffer = malloc(response_len);
|
||||
if(!response_buffer)
|
||||
break;
|
||||
n = snprintf(response_buffer, response_len,
|
||||
"HTTP/1.1 \r\n"
|
||||
"\r\n"
|
||||
/*"0000\r\n"*/);
|
||||
for (i = n; i < response_len; i++) {
|
||||
response_buffer[i] = ' ';
|
||||
}
|
||||
response_len = n;
|
||||
break;
|
||||
case MODE_CHUNKED:
|
||||
response_buffer = build_chunked_response(content_length, &response_len);
|
||||
break;
|
||||
case MODE_ADDCRAP:
|
||||
response_len = content_length+256;
|
||||
response_buffer = malloc(response_len);
|
||||
if(!response_buffer)
|
||||
break;
|
||||
n = snprintf(response_buffer, response_len,
|
||||
"HTTP/1.1 200 OK\r\n"
|
||||
"Server: minihttptestserver\r\n"
|
||||
"Content-Type: text/plain\r\n"
|
||||
"Content-Length: %lu\r\n"
|
||||
"\r\n", content_length);
|
||||
response_len = content_length+n+CRAP_LENGTH;
|
||||
p = realloc(response_buffer, response_len);
|
||||
if(p == NULL) {
|
||||
/* error 500 */
|
||||
free(response_buffer);
|
||||
response_buffer = NULL;
|
||||
break;
|
||||
}
|
||||
response_buffer = p;
|
||||
build_content(response_buffer + n, content_length);
|
||||
build_crap(response_buffer + n + content_length, CRAP_LENGTH);
|
||||
break;
|
||||
case MODE_FAVICON:
|
||||
content_length = FAVICON_LENGTH;
|
||||
response_len = content_length + 256;
|
||||
response_buffer = malloc(response_len);
|
||||
if(!response_buffer)
|
||||
break;
|
||||
n = snprintf(response_buffer, response_len,
|
||||
"HTTP/1.1 200 OK\r\n"
|
||||
"Server: minihttptestserver\r\n"
|
||||
"Content-Type: image/vnd.microsoft.icon\r\n"
|
||||
"Content-Length: %lu\r\n"
|
||||
"\r\n", content_length);
|
||||
/* image/x-icon */
|
||||
build_favicon_content((unsigned char *)(response_buffer + n), content_length);
|
||||
response_len = content_length + n;
|
||||
break;
|
||||
default:
|
||||
response_len = content_length+256;
|
||||
response_buffer = malloc(response_len);
|
||||
if(!response_buffer)
|
||||
break;
|
||||
n = snprintf(response_buffer, response_len,
|
||||
"HTTP/1.1 200 OK\r\n"
|
||||
"Server: minihttptestserver\r\n"
|
||||
"Content-Type: text/plain\r\n"
|
||||
"\r\n");
|
||||
response_len = content_length+n;
|
||||
p = realloc(response_buffer, response_len);
|
||||
if(p == NULL) {
|
||||
/* Error 500 */
|
||||
free(response_buffer);
|
||||
response_buffer = NULL;
|
||||
break;
|
||||
}
|
||||
response_buffer = p;
|
||||
build_content(response_buffer + n, response_len - n);
|
||||
}
|
||||
|
||||
if(response_buffer) {
|
||||
send_response(c, response_buffer, response_len);
|
||||
free(response_buffer);
|
||||
} else {
|
||||
/* Error 500 */
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*/
|
||||
int main(int argc, char * * argv) {
|
||||
int ipv6 = 0;
|
||||
int r, i;
|
||||
unsigned short port = 0;
|
||||
const char * expected_file_name = NULL;
|
||||
|
||||
for(i = 1; i < argc; i++) {
|
||||
if(argv[i][0] == '-') {
|
||||
switch(argv[i][1]) {
|
||||
case '6':
|
||||
ipv6 = 1;
|
||||
break;
|
||||
case 'e':
|
||||
/* write expected file ! */
|
||||
expected_file_name = argv[++i];
|
||||
break;
|
||||
case 'p':
|
||||
/* port */
|
||||
if(++i < argc) {
|
||||
port = (unsigned short)atoi(argv[i]);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "unknown command line switch '%s'\n", argv[i]);
|
||||
}
|
||||
} else {
|
||||
fprintf(stderr, "unknown command line argument '%s'\n", argv[i]);
|
||||
}
|
||||
}
|
||||
|
||||
srand(time(NULL));
|
||||
|
||||
r = server(port, expected_file_name, ipv6);
|
||||
if(r != 0) {
|
||||
printf("*** ERROR ***\n");
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
static int server(unsigned short port, const char * expected_file_name, int ipv6)
|
||||
{
|
||||
int s, c;
|
||||
int i;
|
||||
struct sockaddr_storage server_addr;
|
||||
socklen_t server_addrlen;
|
||||
struct sockaddr_storage client_addr;
|
||||
socklen_t client_addrlen;
|
||||
pid_t pid;
|
||||
int child = 0;
|
||||
int status;
|
||||
struct sigaction sa;
|
||||
|
||||
memset(&sa, 0, sizeof(struct sigaction));
|
||||
|
||||
/*signal(SIGCHLD, handle_signal_chld);*/
|
||||
sa.sa_handler = handle_signal_chld;
|
||||
if(sigaction(SIGCHLD, &sa, NULL) < 0) {
|
||||
perror("sigaction");
|
||||
return 1;
|
||||
}
|
||||
/*signal(SIGINT, handle_signal_int);*/
|
||||
sa.sa_handler = handle_signal_int;
|
||||
if(sigaction(SIGINT, &sa, NULL) < 0) {
|
||||
perror("sigaction");
|
||||
return 1;
|
||||
}
|
||||
|
||||
s = socket(ipv6 ? AF_INET6 : AF_INET, SOCK_STREAM, 0);
|
||||
if(s < 0) {
|
||||
perror("socket");
|
||||
return 1;
|
||||
}
|
||||
memset(&server_addr, 0, sizeof(struct sockaddr_storage));
|
||||
memset(&client_addr, 0, sizeof(struct sockaddr_storage));
|
||||
if(ipv6) {
|
||||
struct sockaddr_in6 * addr = (struct sockaddr_in6 *)&server_addr;
|
||||
addr->sin6_family = AF_INET6;
|
||||
addr->sin6_port = htons(port);
|
||||
addr->sin6_addr = in6addr_loopback;
|
||||
} else {
|
||||
struct sockaddr_in * addr = (struct sockaddr_in *)&server_addr;
|
||||
addr->sin_family = AF_INET;
|
||||
addr->sin_port = htons(port);
|
||||
addr->sin_addr.s_addr = htonl(INADDR_LOOPBACK);
|
||||
}
|
||||
if(bind(s, (struct sockaddr *)&server_addr,
|
||||
ipv6 ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in)) < 0) {
|
||||
perror("bind");
|
||||
return 1;
|
||||
}
|
||||
if(listen(s, 5) < 0) {
|
||||
perror("listen");
|
||||
}
|
||||
if(port == 0) {
|
||||
server_addrlen = sizeof(struct sockaddr_storage);
|
||||
if(getsockname(s, (struct sockaddr *)&server_addr, &server_addrlen) < 0) {
|
||||
perror("getsockname");
|
||||
return 1;
|
||||
}
|
||||
if(ipv6) {
|
||||
struct sockaddr_in6 * addr = (struct sockaddr_in6 *)&server_addr;
|
||||
port = ntohs(addr->sin6_port);
|
||||
} else {
|
||||
struct sockaddr_in * addr = (struct sockaddr_in *)&server_addr;
|
||||
port = ntohs(addr->sin_port);
|
||||
}
|
||||
printf("Listening on port %hu\n", port);
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
/* write expected file */
|
||||
if(expected_file_name) {
|
||||
FILE * f;
|
||||
f = fopen(expected_file_name, "wb");
|
||||
if(f) {
|
||||
char * buffer;
|
||||
buffer = malloc(16*1024);
|
||||
if(buffer == NULL) {
|
||||
fprintf(stderr, "memory allocation error\n");
|
||||
} else {
|
||||
build_content(buffer, 16*1024);
|
||||
i = fwrite(buffer, 1, 16*1024, f);
|
||||
if(i != 16*1024) {
|
||||
fprintf(stderr, "error writing to file %s : %dbytes written (out of %d)\n", expected_file_name, i, 16*1024);
|
||||
}
|
||||
free(buffer);
|
||||
}
|
||||
fclose(f);
|
||||
} else {
|
||||
fprintf(stderr, "error opening file %s for writing\n", expected_file_name);
|
||||
}
|
||||
}
|
||||
|
||||
/* fork() loop */
|
||||
while(!child && !quit) {
|
||||
while(child_to_wait_for > 0) {
|
||||
pid = wait(&status);
|
||||
if(pid < 0) {
|
||||
perror("wait");
|
||||
} else {
|
||||
printf("child(%d) terminated with status %d\n", (int)pid, status);
|
||||
}
|
||||
--child_to_wait_for;
|
||||
}
|
||||
client_addrlen = sizeof(struct sockaddr_storage);
|
||||
c = accept(s, (struct sockaddr *)&client_addr,
|
||||
&client_addrlen);
|
||||
if(c < 0) {
|
||||
if(errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR)
|
||||
continue;
|
||||
perror("accept");
|
||||
return 1;
|
||||
}
|
||||
printf("accept...\n");
|
||||
pid = fork();
|
||||
if(pid < 0) {
|
||||
perror("fork");
|
||||
return 1;
|
||||
} else if(pid == 0) {
|
||||
/* child */
|
||||
child = 1;
|
||||
close(s);
|
||||
s = -1;
|
||||
handle_http_connection(c);
|
||||
}
|
||||
close(c);
|
||||
}
|
||||
if(s >= 0) {
|
||||
close(s);
|
||||
s = -1;
|
||||
}
|
||||
if(!child) {
|
||||
while(child_to_wait_for > 0) {
|
||||
pid = wait(&status);
|
||||
if(pid < 0) {
|
||||
perror("wait");
|
||||
} else {
|
||||
printf("child(%d) terminated with status %d\n", (int)pid, status);
|
||||
}
|
||||
--child_to_wait_for;
|
||||
}
|
||||
printf("Bye...\n");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,120 @@
|
||||
/* $Id: minisoap.c,v 1.36 2025/05/25 21:56:48 nanard Exp $ */
|
||||
/* vim: tabstop=4 shiftwidth=4 noexpandtab
|
||||
* Project : miniupnp
|
||||
* Author : Thomas Bernard
|
||||
* Copyright (c) 2005-2025 Thomas Bernard
|
||||
* This software is subject to the conditions detailed in the
|
||||
* LICENCE file provided in this distribution.
|
||||
*
|
||||
* Minimal SOAP implementation for UPnP protocol.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#ifdef _WIN32
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <io.h>
|
||||
#include <winsock2.h>
|
||||
#include "win32_snprintf.h"
|
||||
#else
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#endif
|
||||
#include "minisoap.h"
|
||||
#include "miniupnpcstrings.h"
|
||||
|
||||
/* only for malloc */
|
||||
#include <stdlib.h>
|
||||
|
||||
/* httpWrite sends the headers and the body to the socket
|
||||
* and returns the number of bytes sent */
|
||||
static int
|
||||
httpWrite(SOCKET fd, const char * body, int bodysize,
|
||||
const char * headers, int headerssize)
|
||||
{
|
||||
int n = 0;
|
||||
/*n = write(fd, headers, headerssize);*/
|
||||
/*if(bodysize>0)
|
||||
n += write(fd, body, bodysize);*/
|
||||
/* Note : my old linksys router only took into account
|
||||
* soap request that are sent into only one packet */
|
||||
char * p;
|
||||
/* TODO: AVOID MALLOC, we could use writev() for that */
|
||||
p = malloc(headerssize+bodysize);
|
||||
if(!p)
|
||||
return -1;
|
||||
memcpy(p, headers, headerssize);
|
||||
memcpy(p+headerssize, body, bodysize);
|
||||
/*n = write(fd, p, headerssize+bodysize);*/
|
||||
n = send(fd, p, headerssize+bodysize, 0);
|
||||
if(n<0) {
|
||||
PRINT_SOCKET_ERROR("send");
|
||||
}
|
||||
/* disable send on the socket */
|
||||
/* draytek routers don't seem to like that... */
|
||||
#if 0
|
||||
#ifdef _WIN32
|
||||
if(shutdown(fd, SD_SEND)<0) {
|
||||
#else
|
||||
if(shutdown(fd, SHUT_WR)<0) { /*SD_SEND*/
|
||||
#endif
|
||||
PRINT_SOCKET_ERROR("shutdown");
|
||||
}
|
||||
#endif
|
||||
free(p);
|
||||
return n;
|
||||
}
|
||||
|
||||
/* self explanatory */
|
||||
int soapPostSubmit(SOCKET fd,
|
||||
const char * url,
|
||||
const char * host,
|
||||
unsigned short port,
|
||||
const char * action,
|
||||
const char * body,
|
||||
const char * httpversion)
|
||||
{
|
||||
char headerbuf[512];
|
||||
int headerssize;
|
||||
char portstr[8];
|
||||
int bodysize = (int)strlen(body);
|
||||
/* We are not using keep-alive HTTP connections.
|
||||
* HTTP/1.1 needs the header Connection: close to do that.
|
||||
* This is the default with HTTP/1.0
|
||||
* Using HTTP/1.1 means we need to support chunked transfer-encoding :
|
||||
* When using HTTP/1.1, the router "BiPAC 7404VNOX" always use chunked
|
||||
* transfer encoding. */
|
||||
/* Connection: close is normally there only in HTTP/1.1 but who knows */
|
||||
portstr[0] = '\0';
|
||||
if(port != 80)
|
||||
sprintf_s(portstr, sizeof(portstr), ":%hu", port);
|
||||
headerssize = sprintf_s(headerbuf, sizeof(headerbuf),
|
||||
"POST %s HTTP/%s\r\n"
|
||||
"Host: %s%s\r\n"
|
||||
"User-Agent: " OS_STRING " " UPNP_VERSION_STRING " MiniUPnPc/" MINIUPNPC_VERSION_STRING "\r\n"
|
||||
"Content-Length: %d\r\n"
|
||||
#if (UPNP_VERSION_MAJOR == 1) && (UPNP_VERSION_MINOR == 0)
|
||||
"Content-Type: text/xml\r\n"
|
||||
#else
|
||||
"Content-Type: text/xml; charset=\"utf-8\"\r\n"
|
||||
#endif
|
||||
"SOAPAction: \"%s\"\r\n"
|
||||
"Connection: close\r\n"
|
||||
"\r\n",
|
||||
url, httpversion, host, portstr, bodysize, action);
|
||||
if ((unsigned int)headerssize >= sizeof(headerbuf))
|
||||
return -1;
|
||||
#ifdef DEBUG
|
||||
/*printf("SOAP request : headersize=%d bodysize=%d\n",
|
||||
headerssize, bodysize);
|
||||
*/
|
||||
printf("SOAP request : POST %s HTTP/%s - Host: %s%s\n",
|
||||
url, httpversion, host, portstr);
|
||||
printf("SOAPAction: \"%s\" - Content-Length: %d\n", action, bodysize);
|
||||
printf("Headers :\n%s", headerbuf);
|
||||
printf("Body :\n%s\n", body);
|
||||
#endif
|
||||
return httpWrite(fd, body, bodysize, headerbuf, headerssize);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
/* $Id: minisoap.h,v 1.6 2018/04/06 10:53:13 nanard Exp $ */
|
||||
/* Project : miniupnp
|
||||
* Author : Thomas Bernard
|
||||
* Copyright (c) 2005-2018 Thomas Bernard
|
||||
* This software is subject to the conditions detailed in the
|
||||
* LICENCE file provided in this distribution. */
|
||||
#ifndef MINISOAP_H_INCLUDED
|
||||
#define MINISOAP_H_INCLUDED
|
||||
|
||||
#include "miniupnpc_socketdef.h"
|
||||
|
||||
/*int httpWrite(int, const char *, int, const char *);*/
|
||||
int soapPostSubmit(SOCKET, const char *, const char *, unsigned short,
|
||||
const char *, const char *, const char *);
|
||||
|
||||
#endif
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,58 @@
|
||||
/* $Id: minissdpc.h,v 1.8 2019/02/10 12:29:23 nanard Exp $ */
|
||||
/* Project: miniupnp
|
||||
* http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
|
||||
* Author: Thomas Bernard
|
||||
* Copyright (c) 2005-2015 Thomas Bernard
|
||||
* This software is subjects to the conditions detailed
|
||||
* in the LICENCE file provided within this distribution */
|
||||
#ifndef MINISSDPC_H_INCLUDED
|
||||
#define MINISSDPC_H_INCLUDED
|
||||
|
||||
#include "miniupnpc_declspec.h"
|
||||
#include "upnpdev.h"
|
||||
|
||||
/* error codes : */
|
||||
#define MINISSDPC_SUCCESS (0)
|
||||
#define MINISSDPC_UNKNOWN_ERROR (-1)
|
||||
#define MINISSDPC_SOCKET_ERROR (-101)
|
||||
#define MINISSDPC_MEMORY_ERROR (-102)
|
||||
#define MINISSDPC_INVALID_INPUT (-103)
|
||||
#define MINISSDPC_INVALID_SERVER_REPLY (-104)
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#if !(defined(_WIN32) || defined(__amigaos__) || defined(__amigaos4__))
|
||||
|
||||
MINIUPNP_LIBSPEC struct UPNPDev *
|
||||
getDevicesFromMiniSSDPD(const char * devtype, const char * socketpath, int * error);
|
||||
|
||||
MINIUPNP_LIBSPEC int
|
||||
connectToMiniSSDPD(const char * socketpath);
|
||||
|
||||
MINIUPNP_LIBSPEC int
|
||||
disconnectFromMiniSSDPD(int s);
|
||||
|
||||
MINIUPNP_LIBSPEC int
|
||||
requestDevicesFromMiniSSDPD(int s, const char * devtype);
|
||||
|
||||
MINIUPNP_LIBSPEC struct UPNPDev *
|
||||
receiveDevicesFromMiniSSDPD(int s, int * error);
|
||||
|
||||
#endif /* !(defined(_WIN32) || defined(__amigaos__) || defined(__amigaos4__)) */
|
||||
|
||||
MINIUPNP_LIBSPEC struct UPNPDev *
|
||||
ssdpDiscoverDevices(const char * const deviceTypes[],
|
||||
int delay, const char * multicastif,
|
||||
int localport,
|
||||
int ipv6, unsigned char ttl,
|
||||
int * error,
|
||||
int searchalltypes);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
if(NOT EXISTS "${CMAKE_CURRENT_LIST_DIR}/libminiupnpc-shared.cmake" OR MINIUPNPC_USE_STATIC_LIBS)
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/miniupnpc-private.cmake")
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/libminiupnpc-static.cmake")
|
||||
else()
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/libminiupnpc-shared.cmake")
|
||||
endif()
|
||||
@@ -0,0 +1,684 @@
|
||||
/* $Id: miniupnpc.c,v 1.167 2025/05/25 21:56:48 nanard Exp $ */
|
||||
/* vim: tabstop=4 shiftwidth=4 noexpandtab
|
||||
* Project : miniupnp
|
||||
* Web : http://miniupnp.free.fr/ or https://miniupnp.tuxfamily.org/
|
||||
* Author : Thomas BERNARD
|
||||
* copyright (c) 2005-2025 Thomas Bernard
|
||||
* This software is subjet to the conditions detailed in the
|
||||
* provided LICENSE file. */
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#ifdef _WIN32
|
||||
/* Win32 Specific includes and defines */
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <winsock2.h>
|
||||
#include <ws2tcpip.h>
|
||||
#include <io.h>
|
||||
#include <iphlpapi.h>
|
||||
#include "win32_snprintf.h"
|
||||
#define strdup _strdup
|
||||
#ifndef strncasecmp
|
||||
#if defined(_MSC_VER) && (_MSC_VER >= 1400)
|
||||
#define strncasecmp _memicmp
|
||||
#else /* defined(_MSC_VER) && (_MSC_VER >= 1400) */
|
||||
#define strncasecmp memicmp
|
||||
#endif /* defined(_MSC_VER) && (_MSC_VER >= 1400) */
|
||||
#endif /* #ifndef strncasecmp */
|
||||
#define MAXHOSTNAMELEN 64
|
||||
#else /* #ifdef _WIN32 */
|
||||
/* Standard POSIX includes */
|
||||
#include <unistd.h>
|
||||
#if defined(__amigaos__) && !defined(__amigaos4__)
|
||||
/* Amiga OS 3 specific stuff */
|
||||
#define socklen_t int
|
||||
#else
|
||||
#include <sys/select.h>
|
||||
#endif
|
||||
#include <sys/socket.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/param.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <netdb.h>
|
||||
#include <net/if.h>
|
||||
#if !defined(__amigaos__) && !defined(__amigaos4__)
|
||||
#include <poll.h>
|
||||
#endif
|
||||
#include <strings.h>
|
||||
#include <errno.h>
|
||||
#define closesocket close
|
||||
#endif /* #else _WIN32 */
|
||||
#ifdef __GNU__
|
||||
#define MAXHOSTNAMELEN 64
|
||||
#endif
|
||||
|
||||
|
||||
#include "miniupnpc.h"
|
||||
#include "minissdpc.h"
|
||||
#include "miniwget.h"
|
||||
#include "miniwget_private.h"
|
||||
#include "minisoap.h"
|
||||
#include "minixml.h"
|
||||
#include "upnpcommands.h"
|
||||
#include "connecthostport.h"
|
||||
#include "addr_is_reserved.h"
|
||||
|
||||
/* compare the beginning of a string with a constant string */
|
||||
#define COMPARE(str, cstr) (0==strncmp(str, cstr, sizeof(cstr) - 1))
|
||||
|
||||
#ifndef MAXHOSTNAMELEN
|
||||
#define MAXHOSTNAMELEN 64
|
||||
#endif
|
||||
|
||||
#define SOAPPREFIX "s"
|
||||
#define SERVICEPREFIX "u"
|
||||
#define SERVICEPREFIX2 'u'
|
||||
|
||||
/* root description parsing */
|
||||
MINIUPNP_LIBSPEC void parserootdesc(const char * buffer, int bufsize, struct IGDdatas * data)
|
||||
{
|
||||
struct xmlparser parser;
|
||||
/* xmlparser object */
|
||||
parser.xmlstart = buffer;
|
||||
parser.xmlsize = bufsize;
|
||||
parser.data = data;
|
||||
parser.starteltfunc = IGDstartelt;
|
||||
parser.endeltfunc = IGDendelt;
|
||||
parser.datafunc = IGDdata;
|
||||
parser.attfunc = 0;
|
||||
parsexml(&parser);
|
||||
#ifdef DEBUG
|
||||
printIGD(data);
|
||||
#endif
|
||||
}
|
||||
|
||||
/* simpleUPnPcommand :
|
||||
* not so simple !
|
||||
* return values :
|
||||
* pointer - OK
|
||||
* NULL - error */
|
||||
char *
|
||||
simpleUPnPcommand(const char * url, const char * service,
|
||||
const char * action, const struct UPNParg * args,
|
||||
int * bufsize)
|
||||
{
|
||||
char hostname[MAXHOSTNAMELEN+1];
|
||||
unsigned short port = 0;
|
||||
char * path;
|
||||
char soapact[128];
|
||||
char soapbody[2048];
|
||||
int soapbodylen;
|
||||
char * buf;
|
||||
int n;
|
||||
int status_code;
|
||||
SOCKET s;
|
||||
|
||||
*bufsize = 0;
|
||||
snprintf(soapact, sizeof(soapact), "%s#%s", service, action);
|
||||
if(args==NULL)
|
||||
{
|
||||
soapbodylen = snprintf(soapbody, sizeof(soapbody),
|
||||
"<?xml version=\"1.0\"?>\r\n"
|
||||
"<" SOAPPREFIX ":Envelope "
|
||||
"xmlns:" SOAPPREFIX "=\"http://schemas.xmlsoap.org/soap/envelope/\" "
|
||||
SOAPPREFIX ":encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">"
|
||||
"<" SOAPPREFIX ":Body>"
|
||||
"<" SERVICEPREFIX ":%s xmlns:" SERVICEPREFIX "=\"%s\">"
|
||||
"</" SERVICEPREFIX ":%s>"
|
||||
"</" SOAPPREFIX ":Body></" SOAPPREFIX ":Envelope>"
|
||||
"\r\n", action, service, action);
|
||||
if ((unsigned int)soapbodylen >= sizeof(soapbody))
|
||||
return NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
char * p;
|
||||
const char * pe, * pv;
|
||||
const char * const pend = soapbody + sizeof(soapbody);
|
||||
soapbodylen = snprintf(soapbody, sizeof(soapbody),
|
||||
"<?xml version=\"1.0\"?>\r\n"
|
||||
"<" SOAPPREFIX ":Envelope "
|
||||
"xmlns:" SOAPPREFIX "=\"http://schemas.xmlsoap.org/soap/envelope/\" "
|
||||
SOAPPREFIX ":encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">"
|
||||
"<" SOAPPREFIX ":Body>"
|
||||
"<" SERVICEPREFIX ":%s xmlns:" SERVICEPREFIX "=\"%s\">",
|
||||
action, service);
|
||||
if ((unsigned int)soapbodylen >= sizeof(soapbody))
|
||||
return NULL;
|
||||
p = soapbody + soapbodylen;
|
||||
while(args->elt)
|
||||
{
|
||||
if(p >= pend) /* check for space to write next byte */
|
||||
return NULL;
|
||||
*(p++) = '<';
|
||||
|
||||
pe = args->elt;
|
||||
while(p < pend && *pe)
|
||||
*(p++) = *(pe++);
|
||||
|
||||
if(p >= pend) /* check for space to write next byte */
|
||||
return NULL;
|
||||
*(p++) = '>';
|
||||
|
||||
if((pv = args->val))
|
||||
{
|
||||
while(p < pend && *pv)
|
||||
*(p++) = *(pv++);
|
||||
}
|
||||
|
||||
if((p+2) > pend) /* check for space to write next 2 bytes */
|
||||
return NULL;
|
||||
*(p++) = '<';
|
||||
*(p++) = '/';
|
||||
|
||||
pe = args->elt;
|
||||
while(p < pend && *pe)
|
||||
*(p++) = *(pe++);
|
||||
|
||||
if(p >= pend) /* check for space to write next byte */
|
||||
return NULL;
|
||||
*(p++) = '>';
|
||||
|
||||
args++;
|
||||
}
|
||||
if((p+4) > pend) /* check for space to write next 4 bytes */
|
||||
return NULL;
|
||||
*(p++) = '<';
|
||||
*(p++) = '/';
|
||||
*(p++) = SERVICEPREFIX2;
|
||||
*(p++) = ':';
|
||||
|
||||
pe = action;
|
||||
while(p < pend && *pe)
|
||||
*(p++) = *(pe++);
|
||||
|
||||
strncpy(p, "></" SOAPPREFIX ":Body></" SOAPPREFIX ":Envelope>\r\n",
|
||||
pend - p);
|
||||
if(soapbody[sizeof(soapbody)-1]) /* strncpy pads buffer with 0s, so if it doesn't end in 0, could not fit full string */
|
||||
return NULL;
|
||||
}
|
||||
if(!parseURL(url, hostname, &port, &path, NULL)) return NULL;
|
||||
s = connecthostport(hostname, port, 0);
|
||||
if(ISINVALID(s)) {
|
||||
/* failed to connect */
|
||||
return NULL;
|
||||
}
|
||||
|
||||
n = soapPostSubmit(s, path, hostname, port, soapact, soapbody, "1.1");
|
||||
if(n<=0) {
|
||||
#ifdef DEBUG
|
||||
printf("Error sending SOAP request\n");
|
||||
#endif
|
||||
closesocket(s);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
buf = getHTTPResponse(s, bufsize, &status_code);
|
||||
#ifdef DEBUG
|
||||
if(*bufsize > 0 && buf)
|
||||
{
|
||||
printf("HTTP %d SOAP Response :\n%.*s\n", status_code, *bufsize, buf);
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("HTTP %d, empty SOAP response. size=%d\n", status_code, *bufsize);
|
||||
}
|
||||
#endif
|
||||
closesocket(s);
|
||||
return buf;
|
||||
}
|
||||
|
||||
/* upnpDiscoverDevices() :
|
||||
* return a chained list of all devices found or NULL if
|
||||
* no devices was found.
|
||||
* It is up to the caller to free the chained list
|
||||
* delay is in millisecond (poll).
|
||||
* UDA v1.1 says :
|
||||
* The TTL for the IP packet SHOULD default to 2 and
|
||||
* SHOULD be configurable. */
|
||||
MINIUPNP_LIBSPEC struct UPNPDev *
|
||||
upnpDiscoverDevices(const char * const deviceTypes[],
|
||||
int delay, const char * multicastif,
|
||||
const char * minissdpdsock, int localport,
|
||||
int ipv6, unsigned char ttl,
|
||||
int * error,
|
||||
int searchalltypes)
|
||||
{
|
||||
struct UPNPDev * tmp;
|
||||
struct UPNPDev * devlist = 0;
|
||||
#if !defined(_WIN32) && !defined(__amigaos__) && !defined(__amigaos4__)
|
||||
int deviceIndex;
|
||||
#endif /* !defined(_WIN32) && !defined(__amigaos__) && !defined(__amigaos4__) */
|
||||
|
||||
if(error)
|
||||
*error = UPNPDISCOVER_UNKNOWN_ERROR;
|
||||
#if !defined(_WIN32) && !defined(__amigaos__) && !defined(__amigaos4__)
|
||||
/* first try to get infos from minissdpd ! */
|
||||
if(!minissdpdsock)
|
||||
minissdpdsock = "/var/run/minissdpd.sock";
|
||||
if(minissdpdsock[0] != '\0') {
|
||||
for(deviceIndex = 0; deviceTypes[deviceIndex]; deviceIndex++) {
|
||||
struct UPNPDev * minissdpd_devlist;
|
||||
int only_rootdevice = 1;
|
||||
minissdpd_devlist = getDevicesFromMiniSSDPD(deviceTypes[deviceIndex],
|
||||
minissdpdsock, 0);
|
||||
if(minissdpd_devlist) {
|
||||
#ifdef DEBUG
|
||||
printf("returned by MiniSSDPD: %s\t%s\n",
|
||||
minissdpd_devlist->st, minissdpd_devlist->descURL);
|
||||
#endif /* DEBUG */
|
||||
if(!strstr(minissdpd_devlist->st, "rootdevice"))
|
||||
only_rootdevice = 0;
|
||||
for(tmp = minissdpd_devlist; tmp->pNext != NULL; tmp = tmp->pNext) {
|
||||
#ifdef DEBUG
|
||||
printf("returned by MiniSSDPD: %s\t%s\n",
|
||||
tmp->pNext->st, tmp->pNext->descURL);
|
||||
#endif /* DEBUG */
|
||||
if(!strstr(tmp->st, "rootdevice"))
|
||||
only_rootdevice = 0;
|
||||
}
|
||||
tmp->pNext = devlist;
|
||||
devlist = minissdpd_devlist;
|
||||
if(!searchalltypes && !only_rootdevice)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
for(tmp = devlist; tmp != NULL; tmp = tmp->pNext) {
|
||||
/* We return what we have found if it was not only a rootdevice */
|
||||
if(!strstr(tmp->st, "rootdevice")) {
|
||||
if(error)
|
||||
*error = UPNPDISCOVER_SUCCESS;
|
||||
return devlist;
|
||||
}
|
||||
}
|
||||
#else /* !defined(_WIN32) && !defined(__amigaos__) && !defined(__amigaos4__) */
|
||||
(void)minissdpdsock; /* unused */
|
||||
#endif /* !defined(_WIN32) && !defined(__amigaos__) && !defined(__amigaos4__) */
|
||||
|
||||
/* direct discovery if minissdpd responses are not sufficient */
|
||||
{
|
||||
struct UPNPDev * discovered_devlist;
|
||||
discovered_devlist = ssdpDiscoverDevices(deviceTypes, delay, multicastif, localport,
|
||||
ipv6, ttl, error, searchalltypes);
|
||||
if(devlist == NULL)
|
||||
devlist = discovered_devlist;
|
||||
else {
|
||||
for(tmp = devlist; tmp->pNext != NULL; tmp = tmp->pNext);
|
||||
tmp->pNext = discovered_devlist;
|
||||
}
|
||||
}
|
||||
return devlist;
|
||||
}
|
||||
|
||||
/* upnpDiscover() Discover IGD device */
|
||||
MINIUPNP_LIBSPEC struct UPNPDev *
|
||||
upnpDiscover(int delay, const char * multicastif,
|
||||
const char * minissdpdsock, int localport,
|
||||
int ipv6, unsigned char ttl,
|
||||
int * error)
|
||||
{
|
||||
static const char * const deviceList[] = {
|
||||
#if 0
|
||||
"urn:schemas-upnp-org:device:InternetGatewayDevice:2",
|
||||
"urn:schemas-upnp-org:service:WANIPConnection:2",
|
||||
#endif
|
||||
"urn:schemas-upnp-org:device:InternetGatewayDevice:1",
|
||||
"urn:schemas-upnp-org:service:WANIPConnection:1",
|
||||
"urn:schemas-upnp-org:service:WANPPPConnection:1",
|
||||
"upnp:rootdevice",
|
||||
/*"ssdp:all",*/
|
||||
0
|
||||
};
|
||||
return upnpDiscoverDevices(deviceList,
|
||||
delay, multicastif, minissdpdsock, localport,
|
||||
ipv6, ttl, error, 0);
|
||||
}
|
||||
|
||||
/* upnpDiscoverAll() Discover all UPnP devices */
|
||||
MINIUPNP_LIBSPEC struct UPNPDev *
|
||||
upnpDiscoverAll(int delay, const char * multicastif,
|
||||
const char * minissdpdsock, int localport,
|
||||
int ipv6, unsigned char ttl,
|
||||
int * error)
|
||||
{
|
||||
static const char * const deviceList[] = {
|
||||
/*"upnp:rootdevice",*/
|
||||
"ssdp:all",
|
||||
0
|
||||
};
|
||||
return upnpDiscoverDevices(deviceList,
|
||||
delay, multicastif, minissdpdsock, localport,
|
||||
ipv6, ttl, error, 0);
|
||||
}
|
||||
|
||||
/* upnpDiscoverDevice() Discover a specific device */
|
||||
MINIUPNP_LIBSPEC struct UPNPDev *
|
||||
upnpDiscoverDevice(const char * device, int delay, const char * multicastif,
|
||||
const char * minissdpdsock, int localport,
|
||||
int ipv6, unsigned char ttl,
|
||||
int * error)
|
||||
{
|
||||
const char * const deviceList[] = {
|
||||
device,
|
||||
0
|
||||
};
|
||||
return upnpDiscoverDevices(deviceList,
|
||||
delay, multicastif, minissdpdsock, localport,
|
||||
ipv6, ttl, error, 0);
|
||||
}
|
||||
|
||||
static char *
|
||||
build_absolute_url(const char * baseurl, const char * descURL,
|
||||
const char * url, unsigned int scope_id)
|
||||
{
|
||||
size_t l, n;
|
||||
char * s;
|
||||
const char * base;
|
||||
char * p;
|
||||
#if defined(IF_NAMESIZE) && !defined(_WIN32)
|
||||
char ifname[IF_NAMESIZE];
|
||||
#else /* defined(IF_NAMESIZE) && !defined(_WIN32) */
|
||||
char scope_str[8];
|
||||
#endif /* defined(IF_NAMESIZE) && !defined(_WIN32) */
|
||||
|
||||
if( (url[0] == 'h')
|
||||
&&(url[1] == 't')
|
||||
&&(url[2] == 't')
|
||||
&&(url[3] == 'p')
|
||||
&&(url[4] == ':')
|
||||
&&(url[5] == '/')
|
||||
&&(url[6] == '/'))
|
||||
return strdup(url);
|
||||
base = (baseurl[0] == '\0') ? descURL : baseurl;
|
||||
n = strlen(base);
|
||||
if(n > 7) {
|
||||
p = strchr(base + 7, '/');
|
||||
if(p)
|
||||
n = p - base;
|
||||
}
|
||||
l = n + strlen(url) + 1;
|
||||
if(url[0] != '/')
|
||||
l++;
|
||||
if(scope_id != 0) {
|
||||
#if defined(IF_NAMESIZE) && !defined(_WIN32)
|
||||
if(if_indextoname(scope_id, ifname)) {
|
||||
l += 3 + strlen(ifname); /* 3 == strlen(%25) */
|
||||
}
|
||||
#else /* defined(IF_NAMESIZE) && !defined(_WIN32) */
|
||||
/* under windows, scope is numerical */
|
||||
l += 3 + snprintf(scope_str, sizeof(scope_str), "%u", scope_id);
|
||||
#endif /* defined(IF_NAMESIZE) && !defined(_WIN32) */
|
||||
}
|
||||
s = malloc(l);
|
||||
if(s == NULL) return NULL;
|
||||
memcpy(s, base, n);
|
||||
if(scope_id != 0) {
|
||||
s[n] = '\0';
|
||||
if(n > 13 && 0 == memcmp(s, "http://[fe80:", 13)) {
|
||||
/* this is a linklocal IPv6 address */
|
||||
p = strchr(s, ']');
|
||||
if(p) {
|
||||
/* insert %25<scope> into URL */
|
||||
#if defined(IF_NAMESIZE) && !defined(_WIN32)
|
||||
memmove(p + 3 + strlen(ifname), p, strlen(p) + 1);
|
||||
memcpy(p, "%25", 3);
|
||||
memcpy(p + 3, ifname, strlen(ifname));
|
||||
n += 3 + strlen(ifname);
|
||||
#else /* defined(IF_NAMESIZE) && !defined(_WIN32) */
|
||||
memmove(p + 3 + strlen(scope_str), p, strlen(p) + 1);
|
||||
memcpy(p, "%25", 3);
|
||||
memcpy(p + 3, scope_str, strlen(scope_str));
|
||||
n += 3 + strlen(scope_str);
|
||||
#endif /* defined(IF_NAMESIZE) && !defined(_WIN32) */
|
||||
}
|
||||
}
|
||||
}
|
||||
if(url[0] != '/')
|
||||
s[n++] = '/';
|
||||
memcpy(s + n, url, l - n);
|
||||
return s;
|
||||
}
|
||||
|
||||
/* Prepare the Urls for usage...
|
||||
*/
|
||||
MINIUPNP_LIBSPEC void
|
||||
GetUPNPUrls(struct UPNPUrls * urls, struct IGDdatas * data,
|
||||
const char * descURL, unsigned int scope_id)
|
||||
{
|
||||
/* strdup descURL */
|
||||
urls->rootdescURL = strdup(descURL);
|
||||
|
||||
/* get description of WANIPConnection */
|
||||
urls->ipcondescURL = build_absolute_url(data->urlbase, descURL,
|
||||
data->first.scpdurl, scope_id);
|
||||
urls->controlURL = build_absolute_url(data->urlbase, descURL,
|
||||
data->first.controlurl, scope_id);
|
||||
urls->controlURL_CIF = build_absolute_url(data->urlbase, descURL,
|
||||
data->CIF.controlurl, scope_id);
|
||||
urls->controlURL_6FC = build_absolute_url(data->urlbase, descURL,
|
||||
data->IPv6FC.controlurl, scope_id);
|
||||
|
||||
#ifdef DEBUG
|
||||
printf("urls->ipcondescURL='%s'\n", urls->ipcondescURL);
|
||||
printf("urls->controlURL='%s'\n", urls->controlURL);
|
||||
printf("urls->controlURL_CIF='%s'\n", urls->controlURL_CIF);
|
||||
printf("urls->controlURL_6FC='%s'\n", urls->controlURL_6FC);
|
||||
#endif
|
||||
}
|
||||
|
||||
MINIUPNP_LIBSPEC void
|
||||
FreeUPNPUrls(struct UPNPUrls * urls)
|
||||
{
|
||||
if(!urls)
|
||||
return;
|
||||
free(urls->controlURL);
|
||||
urls->controlURL = 0;
|
||||
free(urls->ipcondescURL);
|
||||
urls->ipcondescURL = 0;
|
||||
free(urls->controlURL_CIF);
|
||||
urls->controlURL_CIF = 0;
|
||||
free(urls->controlURL_6FC);
|
||||
urls->controlURL_6FC = 0;
|
||||
free(urls->rootdescURL);
|
||||
urls->rootdescURL = 0;
|
||||
}
|
||||
|
||||
int
|
||||
UPNPIGD_IsConnected(struct UPNPUrls * urls, struct IGDdatas * data)
|
||||
{
|
||||
char status[64];
|
||||
unsigned int uptime;
|
||||
status[0] = '\0';
|
||||
UPNP_GetStatusInfo(urls->controlURL, data->first.servicetype,
|
||||
status, &uptime, NULL);
|
||||
if(0 == strcmp("Connected", status))
|
||||
return 1;
|
||||
else if(0 == strcmp("Up", status)) /* Also accept "Up" */
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* UPNP_GetValidIGD() :
|
||||
* return values :
|
||||
* -1 = Internal error
|
||||
* 0 = NO IGD found (UPNP_NO_IGD)
|
||||
* 1 = A valid connected IGD has been found (UPNP_CONNECTED_IGD)
|
||||
* 2 = A valid connected IGD has been found but its
|
||||
* IP address is reserved (non routable) (UPNP_PRIVATEIP_IGD)
|
||||
* 3 = A valid IGD has been found but it reported as
|
||||
* not connected (UPNP_DISCONNECTED_IGD)
|
||||
* 4 = an UPnP device has been found but was not recognized as an IGD
|
||||
* (UPNP_UNKNOWN_DEVICE)
|
||||
*
|
||||
* In any positive non zero return case, the urls and data structures
|
||||
* passed as parameters are set. Don't forget to call FreeUPNPUrls(urls) to
|
||||
* free allocated memory.
|
||||
*/
|
||||
MINIUPNP_LIBSPEC int
|
||||
UPNP_GetValidIGD(struct UPNPDev * devlist,
|
||||
struct UPNPUrls * urls,
|
||||
struct IGDdatas * data,
|
||||
char * lanaddr, int lanaddrlen,
|
||||
char * wanaddr, int wanaddrlen)
|
||||
{
|
||||
struct xml_desc {
|
||||
char lanaddr[40];
|
||||
char wanaddr[40];
|
||||
char * xml;
|
||||
int size;
|
||||
int is_igd;
|
||||
} * desc = NULL;
|
||||
struct UPNPDev * dev;
|
||||
int ndev = 0;
|
||||
int i;
|
||||
int state = -1; /* state 1 : IGD connected. State 2 : connected with reserved IP.
|
||||
* State 3 : IGD. State 4 : anything */
|
||||
int status_code = -1;
|
||||
|
||||
if(!devlist)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
printf("Empty devlist\n");
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
/* counting total number of devices in the list */
|
||||
for(dev = devlist; dev; dev = dev->pNext)
|
||||
ndev++;
|
||||
/* ndev is always > 0 */
|
||||
desc = calloc(ndev, sizeof(struct xml_desc));
|
||||
if(!desc)
|
||||
return -1; /* memory allocation error */
|
||||
/* Step 1 : downloading descriptions and testing type */
|
||||
for(dev = devlist, i = 0; dev; dev = dev->pNext, i++)
|
||||
{
|
||||
/* we should choose an internet gateway device.
|
||||
* with st == urn:schemas-upnp-org:device:InternetGatewayDevice:1 */
|
||||
desc[i].xml = miniwget_getaddr(dev->descURL, &(desc[i].size),
|
||||
desc[i].lanaddr, sizeof(desc[i].lanaddr),
|
||||
dev->scope_id, &status_code);
|
||||
#ifdef DEBUG
|
||||
if(!desc[i].xml)
|
||||
{
|
||||
printf("error getting XML description %s\n", dev->descURL);
|
||||
}
|
||||
#endif
|
||||
if(desc[i].xml)
|
||||
{
|
||||
memset(data, 0, sizeof(struct IGDdatas));
|
||||
memset(urls, 0, sizeof(struct UPNPUrls));
|
||||
parserootdesc(desc[i].xml, desc[i].size, data);
|
||||
if(COMPARE(data->CIF.servicetype,
|
||||
"urn:schemas-upnp-org:service:WANCommonInterfaceConfig:"))
|
||||
{
|
||||
desc[i].is_igd = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
/* iterate the list to find a device depending on state */
|
||||
for(state = 1; state <= 4; state++)
|
||||
{
|
||||
for(dev = devlist, i = 0; dev; dev = dev->pNext, i++)
|
||||
{
|
||||
if(desc[i].xml)
|
||||
{
|
||||
memset(data, 0, sizeof(struct IGDdatas));
|
||||
memset(urls, 0, sizeof(struct UPNPUrls));
|
||||
parserootdesc(desc[i].xml, desc[i].size, data);
|
||||
if(desc[i].is_igd || state >= 4 )
|
||||
{
|
||||
int is_connected;
|
||||
|
||||
GetUPNPUrls(urls, data, dev->descURL, dev->scope_id);
|
||||
|
||||
/* in state 3 and 4 we don't test if device is connected ! */
|
||||
if(state >= 3)
|
||||
goto free_and_return;
|
||||
is_connected = UPNPIGD_IsConnected(urls, data);
|
||||
#ifdef DEBUG
|
||||
printf("UPNPIGD_IsConnected(%s) = %d\n",
|
||||
urls->controlURL, is_connected);
|
||||
#endif
|
||||
/* checks that status is connected AND there is a external IP address assigned */
|
||||
if(is_connected) {
|
||||
if(state >= 2)
|
||||
goto free_and_return;
|
||||
if(UPNP_GetExternalIPAddress(urls->controlURL, data->first.servicetype, desc[i].wanaddr) == 0
|
||||
&& !addr_is_reserved(desc[i].wanaddr))
|
||||
goto free_and_return;
|
||||
}
|
||||
FreeUPNPUrls(urls);
|
||||
if(data->second.servicetype[0] != '\0') {
|
||||
#ifdef DEBUG
|
||||
printf("We tried %s, now we try %s !\n",
|
||||
data->first.servicetype, data->second.servicetype);
|
||||
#endif
|
||||
/* swaping WANPPPConnection and WANIPConnection ! */
|
||||
memcpy(&data->tmp, &data->first, sizeof(struct IGDdatas_service));
|
||||
memcpy(&data->first, &data->second, sizeof(struct IGDdatas_service));
|
||||
memcpy(&data->second, &data->tmp, sizeof(struct IGDdatas_service));
|
||||
GetUPNPUrls(urls, data, dev->descURL, dev->scope_id);
|
||||
is_connected = UPNPIGD_IsConnected(urls, data);
|
||||
#ifdef DEBUG
|
||||
printf("UPNPIGD_IsConnected(%s) = %d\n",
|
||||
urls->controlURL, is_connected);
|
||||
#endif
|
||||
if(is_connected) {
|
||||
if(state >= 2)
|
||||
goto free_and_return;
|
||||
if(UPNP_GetExternalIPAddress(urls->controlURL, data->first.servicetype, desc[i].wanaddr) == 0
|
||||
&& !addr_is_reserved(desc[i].wanaddr))
|
||||
goto free_and_return;
|
||||
}
|
||||
FreeUPNPUrls(urls);
|
||||
}
|
||||
}
|
||||
memset(data, 0, sizeof(struct IGDdatas));
|
||||
}
|
||||
}
|
||||
}
|
||||
state = 0;
|
||||
free_and_return:
|
||||
if (state >= 1 && state <= 4 && i < ndev) {
|
||||
if (lanaddr != NULL)
|
||||
strncpy(lanaddr, desc[i].lanaddr, lanaddrlen);
|
||||
if (wanaddr != NULL)
|
||||
strncpy(wanaddr, desc[i].wanaddr, wanaddrlen);
|
||||
}
|
||||
for(i = 0; i < ndev; i++)
|
||||
free(desc[i].xml);
|
||||
free(desc);
|
||||
return state;
|
||||
}
|
||||
|
||||
/* UPNP_GetIGDFromUrl()
|
||||
* Used when skipping the discovery process.
|
||||
* return value :
|
||||
* 0 - Not ok
|
||||
* 1 - OK */
|
||||
int
|
||||
UPNP_GetIGDFromUrl(const char * rootdescurl,
|
||||
struct UPNPUrls * urls,
|
||||
struct IGDdatas * data,
|
||||
char * lanaddr, int lanaddrlen)
|
||||
{
|
||||
char * descXML;
|
||||
int descXMLsize = 0;
|
||||
|
||||
descXML = miniwget_getaddr(rootdescurl, &descXMLsize,
|
||||
lanaddr, lanaddrlen, 0, NULL);
|
||||
if(descXML) {
|
||||
memset(data, 0, sizeof(struct IGDdatas));
|
||||
memset(urls, 0, sizeof(struct UPNPUrls));
|
||||
parserootdesc(descXML, descXMLsize, data);
|
||||
free(descXML);
|
||||
GetUPNPUrls(urls, data, rootdescurl, 0);
|
||||
return 1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
LIBRARY
|
||||
; miniupnpc library
|
||||
miniupnpc
|
||||
|
||||
EXPORTS
|
||||
; miniupnpc
|
||||
upnpDiscover
|
||||
upnpDiscoverDevice
|
||||
upnpDiscoverDevices
|
||||
upnpDiscoverAll
|
||||
freeUPNPDevlist
|
||||
parserootdesc
|
||||
UPNP_GetValidIGD
|
||||
UPNP_GetIGDFromUrl
|
||||
GetUPNPUrls
|
||||
FreeUPNPUrls
|
||||
; miniwget
|
||||
miniwget
|
||||
miniwget_getaddr
|
||||
; upnpcommands
|
||||
UPNP_GetTotalBytesSent
|
||||
UPNP_GetTotalBytesReceived
|
||||
UPNP_GetTotalPacketsSent
|
||||
UPNP_GetTotalPacketsReceived
|
||||
UPNP_GetStatusInfo
|
||||
UPNP_GetConnectionTypeInfo
|
||||
UPNP_GetExternalIPAddress
|
||||
UPNP_GetLinkLayerMaxBitRates
|
||||
UPNP_AddPortMapping
|
||||
UPNP_AddAnyPortMapping
|
||||
UPNP_DeletePortMapping
|
||||
UPNP_DeletePortMappingRange
|
||||
UPNP_GetPortMappingNumberOfEntries
|
||||
UPNP_GetSpecificPortMappingEntry
|
||||
UPNP_GetGenericPortMappingEntry
|
||||
UPNP_GetListOfPortMappings
|
||||
UPNP_AddPinhole
|
||||
UPNP_CheckPinholeWorking
|
||||
UPNP_UpdatePinhole
|
||||
UPNP_GetPinholePackets
|
||||
UPNP_DeletePinhole
|
||||
UPNP_GetFirewallStatus
|
||||
UPNP_GetOutboundPinholeTimeout
|
||||
; upnperrors
|
||||
strupnperror
|
||||
; portlistingparse
|
||||
ParsePortListing
|
||||
FreePortListing
|
||||
@@ -0,0 +1,305 @@
|
||||
/* $Id: miniupnpc.h,v 1.79 2025/05/25 21:51:11 nanard Exp $ */
|
||||
/* vim: tabstop=4 shiftwidth=4 noexpandtab
|
||||
* Project: miniupnp
|
||||
* http://miniupnp.free.fr/ or https://miniupnp.tuxfamily.org/
|
||||
* Author: Thomas Bernard
|
||||
* Copyright (c) 2005-2025 Thomas Bernard
|
||||
* This software is subjects to the conditions detailed
|
||||
* in the LICENCE file provided within this distribution */
|
||||
#ifndef MINIUPNPC_H_INCLUDED
|
||||
#define MINIUPNPC_H_INCLUDED
|
||||
|
||||
/*! \file miniupnpc.h
|
||||
* \brief Main C API for MiniUPnPc
|
||||
*
|
||||
* Contains functions to discover devices and check for device validity
|
||||
* or connectivity.
|
||||
*
|
||||
* \mainpage MiniUPnPc API documentation
|
||||
* MiniUPnPc (MiniUPnP client) is a library implementing a UPnP
|
||||
* Internet Gateway Device (IGD) control point.
|
||||
*
|
||||
* It should be used by applications that need to listen to incoming
|
||||
* traffic from the internet which are running on a LAN where a
|
||||
* UPnP IGD is running on the router (or gateway).
|
||||
*
|
||||
* See more documentation on the website http://miniupnp.free.fr
|
||||
* or https://miniupnp.tuxfamily.org/ or GitHub :
|
||||
* https://github.com/miniupnp/miniupnp/tree/master/miniupnpc
|
||||
*/
|
||||
#include "miniupnpc_declspec.h"
|
||||
#include "igd_desc_parse.h"
|
||||
#include "upnpdev.h"
|
||||
|
||||
/* error codes : */
|
||||
/*! \brief value for success */
|
||||
#define UPNPDISCOVER_SUCCESS (0)
|
||||
/*! \brief value for unknown error */
|
||||
#define UPNPDISCOVER_UNKNOWN_ERROR (-1)
|
||||
/*! \brief value for a socket error */
|
||||
#define UPNPDISCOVER_SOCKET_ERROR (-101)
|
||||
/*! \brief value for a memory allocation error */
|
||||
#define UPNPDISCOVER_MEMORY_ERROR (-102)
|
||||
|
||||
/*! \brief software version */
|
||||
#define MINIUPNPC_VERSION "2.3.3"
|
||||
/*! \brief C API version */
|
||||
#define MINIUPNPC_API_VERSION 21
|
||||
|
||||
/*! \brief any (ie system chosen) port */
|
||||
#define UPNP_LOCAL_PORT_ANY 0
|
||||
/*! \brief Use as an alias for 1900 for backwards compatibility */
|
||||
#define UPNP_LOCAL_PORT_SAME 1
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Structures definitions : */
|
||||
|
||||
/*!
|
||||
* \brief UPnP method argument
|
||||
*/
|
||||
struct UPNParg {
|
||||
const char * elt; /*!< \brief UPnP argument name */
|
||||
const char * val; /*!< \brief UPnP argument value */
|
||||
};
|
||||
|
||||
/*!
|
||||
* \brief execute a UPnP method (SOAP action)
|
||||
*
|
||||
* \todo error reporting should be improved
|
||||
*
|
||||
* \param[in] url Control URL for the service
|
||||
* \param[in] service service to use
|
||||
* \param[in] action action to call
|
||||
* \param[in] args action arguments
|
||||
* \param[out] bufsize the size of the returned buffer
|
||||
* \return NULL in case of error or the raw XML response
|
||||
*/
|
||||
char *
|
||||
simpleUPnPcommand(const char * url, const char * service,
|
||||
const char * action, const struct UPNParg * args,
|
||||
int * bufsize);
|
||||
|
||||
/*!
|
||||
* \brief Discover UPnP IGD on the network.
|
||||
*
|
||||
* The discovered devices are returned as a chained list.
|
||||
* It is up to the caller to free the list with freeUPNPDevlist().
|
||||
* If available, device list will be obtained from MiniSSDPd.
|
||||
*
|
||||
* \param[in] delay (in millisecond) maximum time for waiting any device
|
||||
* response
|
||||
* \param[in] multicastif If not NULL, used instead of the default
|
||||
* multicast interface for sending SSDP discover packets
|
||||
* \param[in] minissdpdsock Path to minissdpd socket, default is used if
|
||||
* NULL
|
||||
* \param[in] localport Source port to send SSDP packets.
|
||||
* #UPNP_LOCAL_PORT_SAME for 1900 (same as destination port)
|
||||
* #UPNP_LOCAL_PORT_ANY to let system assign a source port
|
||||
* \param[in] ipv6 0 for IPv4, 1 of IPv6
|
||||
* \param[in] ttl should default to 2 as advised by UDA 1.1
|
||||
* \param[out] error error code when NULL is returned
|
||||
* \return NULL or a linked list
|
||||
*/
|
||||
MINIUPNP_LIBSPEC struct UPNPDev *
|
||||
upnpDiscover(int delay, const char * multicastif,
|
||||
const char * minissdpdsock, int localport,
|
||||
int ipv6, unsigned char ttl,
|
||||
int * error);
|
||||
|
||||
/*!
|
||||
* \brief Discover all UPnP devices on the network
|
||||
*
|
||||
* search for "ssdp:all"
|
||||
* \param[in] delay (in millisecond) maximum time for waiting any device
|
||||
* response
|
||||
* \param[in] multicastif If not NULL, used instead of the default
|
||||
* multicast interface for sending SSDP discover packets
|
||||
* \param[in] minissdpdsock Path to minissdpd socket, default is used if
|
||||
* NULL
|
||||
* \param[in] localport Source port to send SSDP packets.
|
||||
* #UPNP_LOCAL_PORT_SAME for 1900 (same as destination port)
|
||||
* #UPNP_LOCAL_PORT_ANY to let system assign a source port
|
||||
* \param[in] ipv6 0 for IPv4, 1 of IPv6
|
||||
* \param[in] ttl should default to 2 as advised by UDA 1.1
|
||||
* \param[out] error error code when NULL is returned
|
||||
* \return NULL or a linked list
|
||||
*/
|
||||
MINIUPNP_LIBSPEC struct UPNPDev *
|
||||
upnpDiscoverAll(int delay, const char * multicastif,
|
||||
const char * minissdpdsock, int localport,
|
||||
int ipv6, unsigned char ttl,
|
||||
int * error);
|
||||
|
||||
/*!
|
||||
* \brief Discover one type of UPnP devices
|
||||
*
|
||||
* \param[in] device device type to search
|
||||
* \param[in] delay (in millisecond) maximum time for waiting any device
|
||||
* response
|
||||
* \param[in] multicastif If not NULL, used instead of the default
|
||||
* multicast interface for sending SSDP discover packets
|
||||
* \param[in] minissdpdsock Path to minissdpd socket, default is used if
|
||||
* NULL
|
||||
* \param[in] localport Source port to send SSDP packets.
|
||||
* #UPNP_LOCAL_PORT_SAME for 1900 (same as destination port)
|
||||
* #UPNP_LOCAL_PORT_ANY to let system assign a source port
|
||||
* \param[in] ipv6 0 for IPv4, 1 of IPv6
|
||||
* \param[in] ttl should default to 2 as advised by UDA 1.1
|
||||
* \param[out] error error code when NULL is returned
|
||||
* \return NULL or a linked list
|
||||
*/
|
||||
MINIUPNP_LIBSPEC struct UPNPDev *
|
||||
upnpDiscoverDevice(const char * device, int delay, const char * multicastif,
|
||||
const char * minissdpdsock, int localport,
|
||||
int ipv6, unsigned char ttl,
|
||||
int * error);
|
||||
|
||||
/*!
|
||||
* \brief Discover one or several type of UPnP devices
|
||||
*
|
||||
* \param[in] deviceTypes array of device types to search (ending with NULL)
|
||||
* \param[in] delay (in millisecond) maximum time for waiting any device
|
||||
* response
|
||||
* \param[in] multicastif If not NULL, used instead of the default
|
||||
* multicast interface for sending SSDP discover packets
|
||||
* \param[in] minissdpdsock Path to minissdpd socket, default is used if
|
||||
* NULL
|
||||
* \param[in] localport Source port to send SSDP packets.
|
||||
* #UPNP_LOCAL_PORT_SAME for 1900 (same as destination port)
|
||||
* #UPNP_LOCAL_PORT_ANY to let system assign a source port
|
||||
* \param[in] ipv6 0 for IPv4, 1 of IPv6
|
||||
* \param[in] ttl should default to 2 as advised by UDA 1.1
|
||||
* \param[out] error error code when NULL is returned
|
||||
* \param[in] searchalltypes 0 to stop with the first type returning results
|
||||
* \return NULL or a linked list
|
||||
*/
|
||||
MINIUPNP_LIBSPEC struct UPNPDev *
|
||||
upnpDiscoverDevices(const char * const deviceTypes[],
|
||||
int delay, const char * multicastif,
|
||||
const char * minissdpdsock, int localport,
|
||||
int ipv6, unsigned char ttl,
|
||||
int * error,
|
||||
int searchalltypes);
|
||||
|
||||
/*!
|
||||
* \brief parse root XML description of a UPnP device
|
||||
*
|
||||
* fill the IGDdatas structure.
|
||||
* \param[in] buffer character buffer containing the XML description
|
||||
* \param[in] bufsize size in bytes of the buffer
|
||||
* \param[out] data IGDdatas structure to fill
|
||||
*/
|
||||
MINIUPNP_LIBSPEC void parserootdesc(const char * buffer, int bufsize, struct IGDdatas * data);
|
||||
|
||||
/*!
|
||||
* \brief structure used to get fast access to urls
|
||||
*/
|
||||
struct UPNPUrls {
|
||||
/*! \brief controlURL of the WANIPConnection */
|
||||
char * controlURL;
|
||||
/*! \brief url of the description of the WANIPConnection */
|
||||
char * ipcondescURL;
|
||||
/*! \brief controlURL of the WANCommonInterfaceConfig */
|
||||
char * controlURL_CIF;
|
||||
/*! \brief controlURL of the WANIPv6FirewallControl */
|
||||
char * controlURL_6FC;
|
||||
/*! \brief url of the root description */
|
||||
char * rootdescURL;
|
||||
};
|
||||
|
||||
/*! \brief NO IGD found */
|
||||
#define UPNP_NO_IGD (0)
|
||||
/*! \brief valid and connected IGD */
|
||||
#define UPNP_CONNECTED_IGD (1)
|
||||
/*! \brief valid and connected IGD but with a reserved address
|
||||
* (non routable) */
|
||||
#define UPNP_PRIVATEIP_IGD (2)
|
||||
/*! \brief valid but not connected IGD */
|
||||
#define UPNP_DISCONNECTED_IGD (3)
|
||||
/*! \brief UPnP device not recognized as an IGD */
|
||||
#define UPNP_UNKNOWN_DEVICE (4)
|
||||
|
||||
/*!
|
||||
* \brief look for a valid and possibly connected IGD in the list
|
||||
*
|
||||
* In any non zero return case, the urls and data structures
|
||||
* passed as parameters are set. Donc forget to call FreeUPNPUrls(urls) to
|
||||
* free allocated memory.
|
||||
* \param[in] devlist A device list obtained with upnpDiscover() /
|
||||
* upnpDiscoverAll() / upnpDiscoverDevice() / upnpDiscoverDevices()
|
||||
* \param[out] urls Urls for the IGD found
|
||||
* \param[out] data datas for the IGD found
|
||||
* \param[out] lanaddr buffer to copy the local address of the host to reach the IGD
|
||||
* \param[in] lanaddrlen size of the lanaddr buffer
|
||||
* \param[out] wanaddr buffer to copy the public address of the IGD
|
||||
* \param[in] wanaddrlen size of the wanaddr buffer
|
||||
* \return #UPNP_NO_IGD / #UPNP_CONNECTED_IGD / #UPNP_PRIVATEIP_IGD /
|
||||
* #UPNP_DISCONNECTED_IGD / #UPNP_UNKNOWN_DEVICE
|
||||
*/
|
||||
MINIUPNP_LIBSPEC int
|
||||
UPNP_GetValidIGD(struct UPNPDev * devlist,
|
||||
struct UPNPUrls * urls,
|
||||
struct IGDdatas * data,
|
||||
char * lanaddr, int lanaddrlen,
|
||||
char * wanaddr, int wanaddrlen);
|
||||
|
||||
/*!
|
||||
* \brief Get IGD URLs and data for URL
|
||||
*
|
||||
* Used when skipping the discovery process.
|
||||
* \param[in] rootdescurl Root description URL of the device
|
||||
* \param[out] urls Urls for the IGD found
|
||||
* \param[out] data datas for the IGD found
|
||||
* \param[out] lanaddr buffer to copy the local address of the host to reach the IGD
|
||||
* \param[in] lanaddrlen size of the lanaddr buffer
|
||||
* \return 0 Not ok / 1 OK
|
||||
*/
|
||||
MINIUPNP_LIBSPEC int
|
||||
UPNP_GetIGDFromUrl(const char * rootdescurl,
|
||||
struct UPNPUrls * urls,
|
||||
struct IGDdatas * data,
|
||||
char * lanaddr, int lanaddrlen);
|
||||
|
||||
/*!
|
||||
* \brief Prepare the URLs for usage
|
||||
*
|
||||
* build absolute URLs from the root description
|
||||
* \param[out] urls URL structure to initialize
|
||||
* \param[in] data datas for the IGD
|
||||
* \param[in] descURL root description URL for the IGD
|
||||
* \param[in] scope_id if not 0, add the scope to the linklocal IPv6
|
||||
* addresses in URLs
|
||||
*/
|
||||
MINIUPNP_LIBSPEC void
|
||||
GetUPNPUrls(struct UPNPUrls * urls, struct IGDdatas * data,
|
||||
const char * descURL, unsigned int scope_id);
|
||||
|
||||
/*!
|
||||
* \brief free the members of a UPNPUrls struct
|
||||
*
|
||||
* All URLs buffers are freed and zeroed
|
||||
* \param[out] urls URL structure to free
|
||||
*/
|
||||
MINIUPNP_LIBSPEC void
|
||||
FreeUPNPUrls(struct UPNPUrls * urls);
|
||||
|
||||
/*!
|
||||
* \brief check the current connection status of an IGD
|
||||
*
|
||||
* it uses UPNP_GetStatusInfo()
|
||||
* \param[in] urls IGD URLs
|
||||
* \param[in] data IGD data
|
||||
* \return 1 Connected / 0 Disconnected
|
||||
*/
|
||||
MINIUPNP_LIBSPEC int UPNPIGD_IsConnected(struct UPNPUrls * urls, struct IGDdatas * data);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
# this template is filled-in by CMake `configure_file(... @ONLY)`
|
||||
# the `@....@` are filled in by CMake configure_file(),
|
||||
# from variables set in your CMakeLists.txt or by CMake itself
|
||||
#
|
||||
# Good tutoral for understanding .pc files:
|
||||
# https://people.freedesktop.org/~dbn/pkg-config-guide.html
|
||||
|
||||
prefix="@CMAKE_INSTALL_PREFIX@"
|
||||
exec_prefix="${prefix}"
|
||||
libdir="${prefix}/lib"
|
||||
includedir="${prefix}/include"
|
||||
|
||||
Name: @PROJECT_NAME@
|
||||
Description: @PROJECT_DESCRIPTION@
|
||||
URL: @PROJECT_HOMEPAGE_URL@
|
||||
Version: @PROJECT_VERSION@
|
||||
Libs: -L"${libdir}" -lminiupnpc
|
||||
Cflags: -I"${includedir}"
|
||||
@@ -0,0 +1,36 @@
|
||||
#include <winver.h>
|
||||
#include "rc_version.h"
|
||||
|
||||
VS_VERSION_INFO VERSIONINFO
|
||||
FILEVERSION LIBMINIUPNPC_MAJOR_VERSION,LIBMINIUPNPC_MINOR_VERSION,LIBMINIUPNPC_MICRO_VERSION,0
|
||||
PRODUCTVERSION LIBMINIUPNPC_MAJOR_VERSION,LIBMINIUPNPC_MINOR_VERSION,LIBMINIUPNPC_MICRO_VERSION,0
|
||||
FILEFLAGSMASK VS_FFI_FILEFLAGSMASK
|
||||
#ifdef _DEBUG
|
||||
FILEFLAGS VS_FF_DEBUG
|
||||
#else
|
||||
FILEFLAGS 0
|
||||
#endif
|
||||
FILEOS VOS__WINDOWS32
|
||||
FILETYPE VFT_DLL
|
||||
FILESUBTYPE VFT2_UNKNOWN // not used
|
||||
BEGIN
|
||||
BLOCK "StringFileInfo"
|
||||
BEGIN
|
||||
BLOCK "04090000" /* Lang = US English, Charset = ASCII */
|
||||
BEGIN
|
||||
VALUE "FileDescription", "MiniUPnPc library\0"
|
||||
VALUE "FileVersion", LIBMINIUPNPC_DOTTED_VERSION "\0"
|
||||
VALUE "InternalName", INTERNAL_NAME
|
||||
VALUE "LegalCopyright", "Copyright (C) Thomas Bernard\0"
|
||||
VALUE "LegalTrademarks", "\0"
|
||||
VALUE "OriginalFilename", INTERNAL_NAME
|
||||
VALUE "ProductName", "MiniUPnPc\0"
|
||||
VALUE "ProductVersion", LIBMINIUPNPC_DOTTED_VERSION "\0"
|
||||
VALUE "Comments", "For more information visit https://miniupnp.tuxfamil.org/\0"
|
||||
END
|
||||
END
|
||||
BLOCK "VarFileInfo"
|
||||
BEGIN
|
||||
VALUE "Translation", 0x0409, 0 /* US English, ASCII */
|
||||
END
|
||||
END
|
||||
@@ -0,0 +1,22 @@
|
||||
#ifndef MINIUPNPC_DECLSPEC_H_INCLUDED
|
||||
#define MINIUPNPC_DECLSPEC_H_INCLUDED
|
||||
|
||||
/*! \file miniupnpc_declspec.h
|
||||
* \brief define #MINIUPNP_LIBSPEC for dll exports and imports */
|
||||
#if defined(_WIN32) && !defined(MINIUPNP_STATICLIB)
|
||||
/* for windows dll */
|
||||
#ifdef MINIUPNP_EXPORTS
|
||||
#define MINIUPNP_LIBSPEC __declspec(dllexport)
|
||||
#else
|
||||
#define MINIUPNP_LIBSPEC __declspec(dllimport)
|
||||
#endif
|
||||
#else
|
||||
#if defined(__GNUC__) && __GNUC__ >= 4
|
||||
/* fix dynlib for OS X 10.9.2 and Apple LLVM version 5.0 */
|
||||
#define MINIUPNP_LIBSPEC __attribute__ ((visibility ("default")))
|
||||
#else
|
||||
#define MINIUPNP_LIBSPEC
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#endif /* MINIUPNPC_DECLSPEC_H_INCLUDED */
|
||||
@@ -0,0 +1,44 @@
|
||||
/* $Id: miniupnpc_socketdef.h,v 1.4 2021/03/02 23:35:29 nanard Exp $ */
|
||||
/* Miniupnp project : http://miniupnp.free.fr/ or https://miniupnp.tuxfamily.org/
|
||||
* Author : Thomas Bernard
|
||||
* Copyright (c) 2018 Thomas Bernard
|
||||
* This software is subject to the conditions detailed in the
|
||||
* LICENCE file provided within this distribution */
|
||||
#ifndef MINIUPNPC_SOCKETDEF_H_INCLUDED
|
||||
#define MINIUPNPC_SOCKETDEF_H_INCLUDED
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
#define ISINVALID(s) (INVALID_SOCKET==(s))
|
||||
|
||||
#else
|
||||
|
||||
#ifndef SOCKET
|
||||
#define SOCKET int
|
||||
#endif
|
||||
#ifndef SSIZE_T
|
||||
#define SSIZE_T ssize_t
|
||||
#endif
|
||||
#ifndef INVALID_SOCKET
|
||||
#define INVALID_SOCKET (-1)
|
||||
#endif
|
||||
#ifndef ISINVALID
|
||||
#define ISINVALID(s) ((s)<0)
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#define MSC_CAST_INT (int)
|
||||
#else
|
||||
#define MSC_CAST_INT
|
||||
#endif
|
||||
|
||||
/* definition of PRINT_SOCKET_ERROR */
|
||||
#ifdef _WIN32
|
||||
#define PRINT_SOCKET_ERROR(x) fprintf(stderr, "Socket error: %s, %d\n", x, WSAGetLastError());
|
||||
#else
|
||||
#define PRINT_SOCKET_ERROR(x) perror(x)
|
||||
#endif
|
||||
|
||||
#endif /* MINIUPNPC_SOCKETDEF_H_INCLUDED */
|
||||
@@ -0,0 +1,743 @@
|
||||
/* $Id: miniupnpcmodule.c,v 1.40 2024/05/09 15:10:29 nanard Exp $*/
|
||||
/* vim: tabstop=4 shiftwidth=4 noexpandtab
|
||||
* Project : miniupnp
|
||||
* Author : Thomas BERNARD
|
||||
* website : https://miniupnp.tuxfamily.org/
|
||||
* copyright (c) 2007-2024 Thomas Bernard
|
||||
* This software is subjet to the conditions detailed in the
|
||||
* provided LICENCE file. */
|
||||
#include <Python.h>
|
||||
#define MINIUPNP_STATICLIB
|
||||
#include <structmember.h>
|
||||
#include "miniupnpc.h"
|
||||
#include "upnpcommands.h"
|
||||
#include "upnperrors.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <winsock2.h>
|
||||
#endif
|
||||
|
||||
/* for compatibility with Python < 2.4 */
|
||||
#ifndef Py_RETURN_NONE
|
||||
#define Py_RETURN_NONE return Py_INCREF(Py_None), Py_None
|
||||
#endif
|
||||
|
||||
#ifndef Py_RETURN_TRUE
|
||||
#define Py_RETURN_TRUE return Py_INCREF(Py_True), Py_True
|
||||
#endif
|
||||
|
||||
#ifndef Py_RETURN_FALSE
|
||||
#define Py_RETURN_FALSE return Py_INCREF(Py_False), Py_False
|
||||
#endif
|
||||
|
||||
/* for compatibility with Python < 3.0 */
|
||||
#ifndef PyVarObject_HEAD_INIT
|
||||
#define PyVarObject_HEAD_INIT(type, size) \
|
||||
PyObject_HEAD_INIT(type) size,
|
||||
#endif
|
||||
|
||||
#ifndef Py_TYPE
|
||||
#define Py_TYPE(ob) (((PyObject*)(ob))->ob_type)
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
PyObject_HEAD
|
||||
/* Type-specific fields go here. */
|
||||
struct UPNPDev * devlist;
|
||||
struct UPNPUrls urls;
|
||||
struct IGDdatas data;
|
||||
unsigned int discoverdelay; /* value passed to upnpDiscover() */
|
||||
unsigned int localport; /* value passed to upnpDiscover() */
|
||||
char lanaddr[40]; /* our ip address on the LAN */
|
||||
char wanaddr[40]; /* the ExternalIPAddress returned by the IGD */
|
||||
char * multicastif;
|
||||
char * minissdpdsocket;
|
||||
} UPnPObject;
|
||||
|
||||
static PyMemberDef UPnP_members[] = {
|
||||
{"lanaddr", T_STRING_INPLACE, offsetof(UPnPObject, lanaddr),
|
||||
READONLY, "ip address on the LAN"
|
||||
},
|
||||
{"wanaddr", T_STRING_INPLACE, offsetof(UPnPObject, wanaddr),
|
||||
READONLY, "public ip address on the WAN"
|
||||
},
|
||||
{"discoverdelay", T_UINT, offsetof(UPnPObject, discoverdelay),
|
||||
0/*READWRITE*/, "value in ms used to wait for SSDP responses"
|
||||
},
|
||||
{"localport", T_UINT, offsetof(UPnPObject, localport),
|
||||
0/*READWRITE*/,
|
||||
"If localport is set to UPNP_LOCAL_PORT_SAME(1) "
|
||||
"SSDP packets will be sent from the source port "
|
||||
"1900 (same as destination port), if set to "
|
||||
"UPNP_LOCAL_PORT_ANY(0) system assign a source "
|
||||
"port, any other value will be attempted as the "
|
||||
"source port"
|
||||
},
|
||||
/* T_STRING is always readonly :( */
|
||||
{"multicastif", T_STRING, offsetof(UPnPObject, multicastif),
|
||||
0, "IP of the network interface to be used for multicast operations"
|
||||
},
|
||||
{"minissdpdsocket", T_STRING, offsetof(UPnPObject, minissdpdsocket),
|
||||
0, "path of the MiniSSDPd unix socket"
|
||||
},
|
||||
{NULL}
|
||||
};
|
||||
|
||||
|
||||
static int UPnP_init(UPnPObject *self, PyObject *args, PyObject *kwds)
|
||||
{
|
||||
char* multicastif = NULL;
|
||||
char* minissdpdsocket = NULL;
|
||||
static char *kwlist[] = {
|
||||
"multicastif", "minissdpdsocket", "discoverdelay",
|
||||
"localport", NULL
|
||||
};
|
||||
|
||||
if(!PyArg_ParseTupleAndKeywords(args, kwds, "|zzII", kwlist,
|
||||
&multicastif,
|
||||
&minissdpdsocket,
|
||||
&self->discoverdelay,
|
||||
&self->localport))
|
||||
return -1;
|
||||
|
||||
if(self->localport>1 &&
|
||||
(self->localport>65534||self->localport<1024)) {
|
||||
PyErr_SetString(PyExc_Exception, "Invalid localport value");
|
||||
return -1;
|
||||
}
|
||||
if(multicastif)
|
||||
self->multicastif = strdup(multicastif);
|
||||
if(minissdpdsocket)
|
||||
self->minissdpdsocket = strdup(minissdpdsocket);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
UPnPObject_dealloc(UPnPObject *self)
|
||||
{
|
||||
freeUPNPDevlist(self->devlist);
|
||||
FreeUPNPUrls(&self->urls);
|
||||
free(self->multicastif);
|
||||
free(self->minissdpdsocket);
|
||||
Py_TYPE(self)->tp_free((PyObject*)self);
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
UPnP_discover(UPnPObject *self)
|
||||
{
|
||||
int error = 0;
|
||||
PyObject *res = NULL;
|
||||
|
||||
if(self->devlist)
|
||||
{
|
||||
freeUPNPDevlist(self->devlist);
|
||||
self->devlist = 0;
|
||||
}
|
||||
Py_BEGIN_ALLOW_THREADS
|
||||
self->devlist = upnpDiscover((int)self->discoverdelay/*timeout in ms*/,
|
||||
self->multicastif,
|
||||
self->minissdpdsocket,
|
||||
(int)self->localport,
|
||||
0/*ip v6*/,
|
||||
2/* TTL */,
|
||||
&error);
|
||||
Py_END_ALLOW_THREADS
|
||||
/* Py_RETURN_NONE ??? */
|
||||
if (self->devlist != NULL) {
|
||||
struct UPNPDev * dev;
|
||||
int i = 0;
|
||||
|
||||
for(dev = self->devlist; dev; dev = dev->pNext)
|
||||
i++;
|
||||
res = Py_BuildValue("i", i);
|
||||
return res;
|
||||
} else {
|
||||
PyErr_SetString(PyExc_Exception, strupnperror(error));
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
UPnP_selectigd(UPnPObject *self, PyObject *args)
|
||||
{
|
||||
const char * rootDescUrl = NULL;
|
||||
int r;
|
||||
if(!PyArg_ParseTuple(args, "|z", &rootDescUrl))
|
||||
return NULL;
|
||||
Py_BEGIN_ALLOW_THREADS
|
||||
if (rootDescUrl == NULL) {
|
||||
r = UPNP_GetValidIGD(self->devlist, &self->urls, &self->data,
|
||||
self->lanaddr, sizeof(self->lanaddr),
|
||||
self->wanaddr, sizeof(self->wanaddr));
|
||||
} else {
|
||||
r = UPNP_GetIGDFromUrl(rootDescUrl, &self->urls, &self->data,
|
||||
self->lanaddr, sizeof(self->lanaddr));
|
||||
}
|
||||
Py_END_ALLOW_THREADS
|
||||
if(r)
|
||||
{
|
||||
return Py_BuildValue("s", self->urls.controlURL);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* TODO: have our own exception type ! */
|
||||
PyErr_SetString(PyExc_Exception, "No UPnP device discovered");
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
UPnP_totalbytesent(UPnPObject *self)
|
||||
{
|
||||
UNSIGNED_INTEGER i;
|
||||
Py_BEGIN_ALLOW_THREADS
|
||||
i = UPNP_GetTotalBytesSent(self->urls.controlURL_CIF,
|
||||
self->data.CIF.servicetype);
|
||||
Py_END_ALLOW_THREADS
|
||||
#if (PY_MAJOR_VERSION >= 3) || (PY_MAJOR_VERSION == 2 && PY_MINOR_VERSION > 3)
|
||||
return Py_BuildValue("I", i);
|
||||
#else
|
||||
return Py_BuildValue("i", (int)i);
|
||||
#endif
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
UPnP_totalbytereceived(UPnPObject *self)
|
||||
{
|
||||
UNSIGNED_INTEGER i;
|
||||
Py_BEGIN_ALLOW_THREADS
|
||||
i = UPNP_GetTotalBytesReceived(self->urls.controlURL_CIF,
|
||||
self->data.CIF.servicetype);
|
||||
Py_END_ALLOW_THREADS
|
||||
#if (PY_MAJOR_VERSION >= 3) || (PY_MAJOR_VERSION == 2 && PY_MINOR_VERSION > 3)
|
||||
return Py_BuildValue("I", i);
|
||||
#else
|
||||
return Py_BuildValue("i", (int)i);
|
||||
#endif
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
UPnP_totalpacketsent(UPnPObject *self)
|
||||
{
|
||||
UNSIGNED_INTEGER i;
|
||||
Py_BEGIN_ALLOW_THREADS
|
||||
i = UPNP_GetTotalPacketsSent(self->urls.controlURL_CIF,
|
||||
self->data.CIF.servicetype);
|
||||
Py_END_ALLOW_THREADS
|
||||
#if (PY_MAJOR_VERSION >= 3) || (PY_MAJOR_VERSION == 2 && PY_MINOR_VERSION > 3)
|
||||
return Py_BuildValue("I", i);
|
||||
#else
|
||||
return Py_BuildValue("i", (int)i);
|
||||
#endif
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
UPnP_totalpacketreceived(UPnPObject *self)
|
||||
{
|
||||
UNSIGNED_INTEGER i;
|
||||
Py_BEGIN_ALLOW_THREADS
|
||||
i = UPNP_GetTotalPacketsReceived(self->urls.controlURL_CIF,
|
||||
self->data.CIF.servicetype);
|
||||
Py_END_ALLOW_THREADS
|
||||
#if (PY_MAJOR_VERSION >= 3) || (PY_MAJOR_VERSION == 2 && PY_MINOR_VERSION > 3)
|
||||
return Py_BuildValue("I", i);
|
||||
#else
|
||||
return Py_BuildValue("i", (int)i);
|
||||
#endif
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
UPnP_statusinfo(UPnPObject *self)
|
||||
{
|
||||
char status[64];
|
||||
char lastconnerror[64];
|
||||
unsigned int uptime = 0;
|
||||
int r;
|
||||
status[0] = '\0';
|
||||
lastconnerror[0] = '\0';
|
||||
Py_BEGIN_ALLOW_THREADS
|
||||
r = UPNP_GetStatusInfo(self->urls.controlURL, self->data.first.servicetype,
|
||||
status, &uptime, lastconnerror);
|
||||
Py_END_ALLOW_THREADS
|
||||
if(r==UPNPCOMMAND_SUCCESS) {
|
||||
#if (PY_MAJOR_VERSION >= 3) || (PY_MAJOR_VERSION == 2 && PY_MINOR_VERSION > 3)
|
||||
return Py_BuildValue("(s,I,s)", status, uptime, lastconnerror);
|
||||
#else
|
||||
return Py_BuildValue("(s,i,s)", status, (int)uptime, lastconnerror);
|
||||
#endif
|
||||
} else {
|
||||
/* TODO: have our own exception type ! */
|
||||
PyErr_SetString(PyExc_Exception, strupnperror(r));
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
UPnP_connectiontype(UPnPObject *self)
|
||||
{
|
||||
char connectionType[64];
|
||||
int r;
|
||||
connectionType[0] = '\0';
|
||||
Py_BEGIN_ALLOW_THREADS
|
||||
r = UPNP_GetConnectionTypeInfo(self->urls.controlURL,
|
||||
self->data.first.servicetype,
|
||||
connectionType);
|
||||
Py_END_ALLOW_THREADS
|
||||
if(r==UPNPCOMMAND_SUCCESS) {
|
||||
return Py_BuildValue("s", connectionType);
|
||||
} else {
|
||||
/* TODO: have our own exception type ! */
|
||||
PyErr_SetString(PyExc_Exception, strupnperror(r));
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
UPnP_externalipaddress(UPnPObject *self)
|
||||
{
|
||||
char externalIPAddress[40];
|
||||
int r;
|
||||
externalIPAddress[0] = '\0';
|
||||
Py_BEGIN_ALLOW_THREADS
|
||||
r = UPNP_GetExternalIPAddress(self->urls.controlURL,
|
||||
self->data.first.servicetype,
|
||||
externalIPAddress);
|
||||
Py_END_ALLOW_THREADS
|
||||
if(r==UPNPCOMMAND_SUCCESS) {
|
||||
return Py_BuildValue("s", externalIPAddress);
|
||||
} else {
|
||||
/* TODO: have our own exception type ! */
|
||||
PyErr_SetString(PyExc_Exception, strupnperror(r));
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/* AddPortMapping(externalPort, protocol, internalHost, internalPort, desc,
|
||||
* remoteHost, leaseDuration)
|
||||
* protocol is 'UDP' or 'TCP' */
|
||||
static PyObject *
|
||||
UPnP_addportmapping(UPnPObject *self, PyObject *args)
|
||||
{
|
||||
char extPort[6];
|
||||
unsigned short ePort;
|
||||
char inPort[6];
|
||||
unsigned short iPort;
|
||||
const char * proto;
|
||||
const char * host;
|
||||
const char * desc;
|
||||
const char * remoteHost;
|
||||
unsigned int intLeaseDuration = 0;
|
||||
char strLeaseDuration[12];
|
||||
int r;
|
||||
#if (PY_MAJOR_VERSION >= 3) || (PY_MAJOR_VERSION == 2 && PY_MINOR_VERSION > 3)
|
||||
if (!PyArg_ParseTuple(args, "HssHzz|I", &ePort, &proto,
|
||||
&host, &iPort, &desc, &remoteHost, &intLeaseDuration))
|
||||
#else
|
||||
if (!PyArg_ParseTuple(args, "HssHzz|i", &ePort, &proto,
|
||||
&host, &iPort, &desc, &remoteHost, (int *)&intLeaseDuration))
|
||||
#endif
|
||||
return NULL;
|
||||
Py_BEGIN_ALLOW_THREADS
|
||||
sprintf(extPort, "%hu", ePort);
|
||||
sprintf(inPort, "%hu", iPort);
|
||||
sprintf(strLeaseDuration, "%u", intLeaseDuration);
|
||||
r = UPNP_AddPortMapping(self->urls.controlURL, self->data.first.servicetype,
|
||||
extPort, inPort, host, desc, proto,
|
||||
remoteHost, strLeaseDuration);
|
||||
Py_END_ALLOW_THREADS
|
||||
if(r==UPNPCOMMAND_SUCCESS)
|
||||
{
|
||||
Py_RETURN_TRUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO: RAISE an Exception. See upnpcommands.h for errors codes.
|
||||
// upnperrors.c
|
||||
//Py_RETURN_FALSE;
|
||||
/* TODO: have our own exception type ! */
|
||||
PyErr_SetString(PyExc_Exception, strupnperror(r));
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/* AddAnyPortMapping(externalPort, protocol, internalHost, internalPort, desc,
|
||||
* remoteHost)
|
||||
* protocol is 'UDP' or 'TCP' */
|
||||
static PyObject *
|
||||
UPnP_addanyportmapping(UPnPObject *self, PyObject *args)
|
||||
{
|
||||
char extPort[6];
|
||||
unsigned short ePort;
|
||||
char inPort[6];
|
||||
unsigned short iPort;
|
||||
char reservedPort[6];
|
||||
const char * proto;
|
||||
const char * host;
|
||||
const char * desc;
|
||||
const char * remoteHost;
|
||||
const char * leaseDuration = "0";
|
||||
int r;
|
||||
if (!PyArg_ParseTuple(args, "HssHzz", &ePort, &proto, &host, &iPort, &desc, &remoteHost))
|
||||
return NULL;
|
||||
Py_BEGIN_ALLOW_THREADS
|
||||
sprintf(extPort, "%hu", ePort);
|
||||
sprintf(inPort, "%hu", iPort);
|
||||
r = UPNP_AddAnyPortMapping(self->urls.controlURL, self->data.first.servicetype,
|
||||
extPort, inPort, host, desc, proto,
|
||||
remoteHost, leaseDuration, reservedPort);
|
||||
Py_END_ALLOW_THREADS
|
||||
if(r==UPNPCOMMAND_SUCCESS) {
|
||||
return Py_BuildValue("i", atoi(reservedPort));
|
||||
} else {
|
||||
/* TODO: have our own exception type ! */
|
||||
PyErr_SetString(PyExc_Exception, strupnperror(r));
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* DeletePortMapping(extPort, proto, removeHost='')
|
||||
* proto = 'UDP', 'TCP' */
|
||||
static PyObject *
|
||||
UPnP_deleteportmapping(UPnPObject *self, PyObject *args)
|
||||
{
|
||||
char extPort[6];
|
||||
unsigned short ePort;
|
||||
const char * proto;
|
||||
const char * remoteHost = "";
|
||||
int r;
|
||||
if(!PyArg_ParseTuple(args, "Hs|z", &ePort, &proto, &remoteHost))
|
||||
return NULL;
|
||||
Py_BEGIN_ALLOW_THREADS
|
||||
sprintf(extPort, "%hu", ePort);
|
||||
r = UPNP_DeletePortMapping(self->urls.controlURL, self->data.first.servicetype,
|
||||
extPort, proto, remoteHost);
|
||||
Py_END_ALLOW_THREADS
|
||||
if(r==UPNPCOMMAND_SUCCESS) {
|
||||
Py_RETURN_TRUE;
|
||||
} else {
|
||||
/* TODO: have our own exception type ! */
|
||||
PyErr_SetString(PyExc_Exception, strupnperror(r));
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/* DeletePortMappingRange(extPort, proto, removeHost='')
|
||||
* proto = 'UDP', 'TCP' */
|
||||
static PyObject *
|
||||
UPnP_deleteportmappingrange(UPnPObject *self, PyObject *args)
|
||||
{
|
||||
char extPortStart[6];
|
||||
unsigned short ePortStart;
|
||||
char extPortEnd[6];
|
||||
unsigned short ePortEnd;
|
||||
const char * proto;
|
||||
unsigned char manage;
|
||||
char manageStr[6];
|
||||
int r;
|
||||
if(!PyArg_ParseTuple(args, "HHsb", &ePortStart, &ePortEnd, &proto, &manage))
|
||||
return NULL;
|
||||
Py_BEGIN_ALLOW_THREADS
|
||||
sprintf(extPortStart, "%hu", ePortStart);
|
||||
sprintf(extPortEnd, "%hu", ePortEnd);
|
||||
sprintf(manageStr, "%hu", (unsigned short)manage);
|
||||
r = UPNP_DeletePortMappingRange(self->urls.controlURL, self->data.first.servicetype,
|
||||
extPortStart, extPortEnd, proto, manageStr);
|
||||
Py_END_ALLOW_THREADS
|
||||
if(r==UPNPCOMMAND_SUCCESS) {
|
||||
Py_RETURN_TRUE;
|
||||
} else {
|
||||
/* TODO: have our own exception type ! */
|
||||
PyErr_SetString(PyExc_Exception, strupnperror(r));
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
UPnP_getportmappingnumberofentries(UPnPObject *self)
|
||||
{
|
||||
unsigned int n = 0;
|
||||
int r;
|
||||
Py_BEGIN_ALLOW_THREADS
|
||||
r = UPNP_GetPortMappingNumberOfEntries(self->urls.controlURL,
|
||||
self->data.first.servicetype,
|
||||
&n);
|
||||
Py_END_ALLOW_THREADS
|
||||
if(r==UPNPCOMMAND_SUCCESS) {
|
||||
#if (PY_MAJOR_VERSION >= 3) || (PY_MAJOR_VERSION == 2 && PY_MINOR_VERSION > 3)
|
||||
return Py_BuildValue("I", n);
|
||||
#else
|
||||
return Py_BuildValue("i", (int)n);
|
||||
#endif
|
||||
} else {
|
||||
/* TODO: have our own exception type ! */
|
||||
PyErr_SetString(PyExc_Exception, strupnperror(r));
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/* GetSpecificPortMapping(ePort, proto, remoteHost='')
|
||||
* proto = 'UDP' or 'TCP' */
|
||||
static PyObject *
|
||||
UPnP_getspecificportmapping(UPnPObject *self, PyObject *args)
|
||||
{
|
||||
char extPort[6];
|
||||
unsigned short ePort;
|
||||
const char * proto;
|
||||
const char * remoteHost = "";
|
||||
char intClient[40];
|
||||
char intPort[6];
|
||||
unsigned short iPort;
|
||||
char desc[80];
|
||||
char enabled[4];
|
||||
char leaseDuration[16];
|
||||
if(!PyArg_ParseTuple(args, "Hs|z", &ePort, &proto, &remoteHost))
|
||||
return NULL;
|
||||
extPort[0] = '\0'; intClient[0] = '\0'; intPort[0] = '\0';
|
||||
desc[0] = '\0'; enabled[0] = '\0'; leaseDuration[0] = '\0';
|
||||
Py_BEGIN_ALLOW_THREADS
|
||||
sprintf(extPort, "%hu", ePort);
|
||||
UPNP_GetSpecificPortMappingEntry(self->urls.controlURL,
|
||||
self->data.first.servicetype,
|
||||
extPort, proto, remoteHost,
|
||||
intClient, intPort,
|
||||
desc, enabled, leaseDuration);
|
||||
Py_END_ALLOW_THREADS
|
||||
if(intClient[0])
|
||||
{
|
||||
iPort = (unsigned short)atoi(intPort);
|
||||
return Py_BuildValue("(s,H,s,O,i)",
|
||||
intClient, iPort, desc,
|
||||
PyBool_FromLong(atoi(enabled)),
|
||||
atoi(leaseDuration));
|
||||
}
|
||||
else
|
||||
{
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
}
|
||||
|
||||
/* GetGenericPortMapping(index) */
|
||||
static PyObject *
|
||||
UPnP_getgenericportmapping(UPnPObject *self, PyObject *args)
|
||||
{
|
||||
int i, r;
|
||||
char index[8];
|
||||
char intClient[40];
|
||||
char intPort[6];
|
||||
unsigned short iPort;
|
||||
char extPort[6];
|
||||
unsigned short ePort;
|
||||
char protocol[4];
|
||||
char desc[80];
|
||||
char enabled[6];
|
||||
char rHost[64];
|
||||
char duration[16]; /* lease duration */
|
||||
unsigned int dur;
|
||||
if(!PyArg_ParseTuple(args, "i", &i))
|
||||
return NULL;
|
||||
Py_BEGIN_ALLOW_THREADS
|
||||
snprintf(index, sizeof(index), "%d", i);
|
||||
rHost[0] = '\0'; enabled[0] = '\0';
|
||||
duration[0] = '\0'; desc[0] = '\0';
|
||||
extPort[0] = '\0'; intPort[0] = '\0'; intClient[0] = '\0';
|
||||
r = UPNP_GetGenericPortMappingEntry(self->urls.controlURL,
|
||||
self->data.first.servicetype,
|
||||
index,
|
||||
extPort, intClient, intPort,
|
||||
protocol, desc, enabled, rHost,
|
||||
duration);
|
||||
Py_END_ALLOW_THREADS
|
||||
if(r==UPNPCOMMAND_SUCCESS)
|
||||
{
|
||||
ePort = (unsigned short)atoi(extPort);
|
||||
iPort = (unsigned short)atoi(intPort);
|
||||
dur = (unsigned int)strtoul(duration, 0, 0);
|
||||
#if (PY_MAJOR_VERSION >= 3) || (PY_MAJOR_VERSION == 2 && PY_MINOR_VERSION > 3)
|
||||
return Py_BuildValue("(H,s,(s,H),s,s,s,I)",
|
||||
ePort, protocol, intClient, iPort,
|
||||
desc, enabled, rHost, dur);
|
||||
#else
|
||||
return Py_BuildValue("(i,s,(s,i),s,s,s,i)",
|
||||
(int)ePort, protocol, intClient, (int)iPort,
|
||||
desc, enabled, rHost, (int)dur);
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
}
|
||||
|
||||
/* miniupnpc.UPnP object Method Table */
|
||||
static PyMethodDef UPnP_methods[] = {
|
||||
{"discover", (PyCFunction)UPnP_discover, METH_NOARGS,
|
||||
"discover UPnP IGD devices on the network"
|
||||
},
|
||||
{"selectigd", (PyCFunction)UPnP_selectigd, METH_VARARGS,
|
||||
"select a valid UPnP IGD among discovered devices"
|
||||
},
|
||||
{"totalbytesent", (PyCFunction)UPnP_totalbytesent, METH_NOARGS,
|
||||
"return the total number of bytes sent by UPnP IGD"
|
||||
},
|
||||
{"totalbytereceived", (PyCFunction)UPnP_totalbytereceived, METH_NOARGS,
|
||||
"return the total number of bytes received by UPnP IGD"
|
||||
},
|
||||
{"totalpacketsent", (PyCFunction)UPnP_totalpacketsent, METH_NOARGS,
|
||||
"return the total number of packets sent by UPnP IGD"
|
||||
},
|
||||
{"totalpacketreceived", (PyCFunction)UPnP_totalpacketreceived, METH_NOARGS,
|
||||
"return the total number of packets received by UPnP IGD"
|
||||
},
|
||||
{"statusinfo", (PyCFunction)UPnP_statusinfo, METH_NOARGS,
|
||||
"return status and uptime"
|
||||
},
|
||||
{"connectiontype", (PyCFunction)UPnP_connectiontype, METH_NOARGS,
|
||||
"return IGD WAN connection type"
|
||||
},
|
||||
{"externalipaddress", (PyCFunction)UPnP_externalipaddress, METH_NOARGS,
|
||||
"return external IP address"
|
||||
},
|
||||
{"addportmapping", (PyCFunction)UPnP_addportmapping, METH_VARARGS,
|
||||
"add a port mapping"
|
||||
},
|
||||
{"addanyportmapping", (PyCFunction)UPnP_addanyportmapping, METH_VARARGS,
|
||||
"add a port mapping, IGD to select alternative if necessary"
|
||||
},
|
||||
{"deleteportmapping", (PyCFunction)UPnP_deleteportmapping, METH_VARARGS,
|
||||
"delete a port mapping"
|
||||
},
|
||||
{"deleteportmappingrange", (PyCFunction)UPnP_deleteportmappingrange, METH_VARARGS,
|
||||
"delete a range of port mappings"
|
||||
},
|
||||
{"getportmappingnumberofentries", (PyCFunction)UPnP_getportmappingnumberofentries, METH_NOARGS,
|
||||
"-- non standard --"
|
||||
},
|
||||
{"getspecificportmapping", (PyCFunction)UPnP_getspecificportmapping, METH_VARARGS,
|
||||
"get details about a specific port mapping entry"
|
||||
},
|
||||
{"getgenericportmapping", (PyCFunction)UPnP_getgenericportmapping, METH_VARARGS,
|
||||
"get all details about the port mapping at index"
|
||||
},
|
||||
{NULL} /* Sentinel */
|
||||
};
|
||||
|
||||
static PyTypeObject UPnPType = {
|
||||
PyVarObject_HEAD_INIT(NULL,
|
||||
0) /*ob_size*/
|
||||
"miniupnpc.UPnP", /*tp_name*/
|
||||
sizeof(UPnPObject), /*tp_basicsize*/
|
||||
0, /*tp_itemsize*/
|
||||
(destructor)UPnPObject_dealloc,/*tp_dealloc*/
|
||||
0, /*tp_print*/
|
||||
0, /*tp_getattr*/
|
||||
0, /*tp_setattr*/
|
||||
0, /*tp_compare*/
|
||||
0, /*tp_repr*/
|
||||
0, /*tp_as_number*/
|
||||
0, /*tp_as_sequence*/
|
||||
0, /*tp_as_mapping*/
|
||||
0, /*tp_hash */
|
||||
0, /*tp_call*/
|
||||
0, /*tp_str*/
|
||||
0, /*tp_getattro*/
|
||||
0, /*tp_setattro*/
|
||||
0, /*tp_as_buffer*/
|
||||
Py_TPFLAGS_DEFAULT, /*tp_flags*/
|
||||
"UPnP objects", /* tp_doc */
|
||||
0, /* tp_traverse */
|
||||
0, /* tp_clear */
|
||||
0, /* tp_richcompare */
|
||||
0, /* tp_weaklistoffset */
|
||||
0, /* tp_iter */
|
||||
0, /* tp_iternext */
|
||||
UPnP_methods, /* tp_methods */
|
||||
UPnP_members, /* tp_members */
|
||||
0, /* tp_getset */
|
||||
0, /* tp_base */
|
||||
0, /* tp_dict */
|
||||
0, /* tp_descr_get */
|
||||
0, /* tp_descr_set */
|
||||
0, /* tp_dictoffset */
|
||||
(initproc)UPnP_init, /* tp_init */
|
||||
0, /* tp_alloc */
|
||||
#ifndef _WIN32
|
||||
PyType_GenericNew,/*UPnP_new,*/ /* tp_new */
|
||||
#else
|
||||
0, /* tp_new */
|
||||
#endif
|
||||
0, /* tp_free */
|
||||
};
|
||||
|
||||
/* module methods */
|
||||
static PyMethodDef miniupnpc_methods[] = {
|
||||
{NULL} /* Sentinel */
|
||||
};
|
||||
|
||||
#if PY_MAJOR_VERSION >= 3
|
||||
static struct PyModuleDef moduledef = {
|
||||
PyModuleDef_HEAD_INIT,
|
||||
"miniupnpc", /* m_name */
|
||||
"miniupnpc module.", /* m_doc */
|
||||
-1, /* m_size */
|
||||
miniupnpc_methods, /* m_methods */
|
||||
NULL, /* m_reload */
|
||||
NULL, /* m_traverse */
|
||||
NULL, /* m_clear */
|
||||
NULL, /* m_free */
|
||||
};
|
||||
#endif
|
||||
|
||||
#ifndef PyMODINIT_FUNC /* declarations for DLL import/export */
|
||||
#define PyMODINIT_FUNC void
|
||||
#endif
|
||||
|
||||
PyMODINIT_FUNC
|
||||
#if PY_MAJOR_VERSION >= 3
|
||||
PyInit_miniupnpc(void)
|
||||
#else
|
||||
initminiupnpc(void)
|
||||
#endif
|
||||
{
|
||||
PyObject* m;
|
||||
|
||||
#ifdef _WIN32
|
||||
/* initialize Winsock. */
|
||||
WSADATA wsaData;
|
||||
int nResult = WSAStartup(MAKEWORD(2,2), &wsaData);
|
||||
if (nResult != 0)
|
||||
{
|
||||
/* error code could be WSASYSNOTREADY WSASYSNOTREADY
|
||||
* WSASYSNOTREADY WSASYSNOTREADY WSASYSNOTREADY */
|
||||
#if PY_MAJOR_VERSION >= 3
|
||||
return 0;
|
||||
#else
|
||||
return;
|
||||
#endif
|
||||
}
|
||||
|
||||
UPnPType.tp_new = PyType_GenericNew;
|
||||
#endif
|
||||
if (PyType_Ready(&UPnPType) < 0)
|
||||
#if PY_MAJOR_VERSION >= 3
|
||||
return 0;
|
||||
#else
|
||||
return;
|
||||
#endif
|
||||
|
||||
#if PY_MAJOR_VERSION >= 3
|
||||
m = PyModule_Create(&moduledef);
|
||||
#else
|
||||
m = Py_InitModule3("miniupnpc", miniupnpc_methods,
|
||||
"miniupnpc module.");
|
||||
#endif
|
||||
|
||||
Py_INCREF(&UPnPType);
|
||||
PyModule_AddObject(m, "UPnP", (PyObject *)&UPnPType);
|
||||
|
||||
#if PY_MAJOR_VERSION >= 3
|
||||
return m;
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
#ifndef MINIUPNPCSTRINGS_H_INCLUDED
|
||||
#define MINIUPNPCSTRINGS_H_INCLUDED
|
||||
|
||||
#define OS_STRING "MSWindows"
|
||||
#define MINIUPNPC_VERSION_STRING "2.3.3"
|
||||
#define UPNP_VERSION_STRING "2.3.3"
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,19 @@
|
||||
#ifndef MINIUPNPCSTRINGS_H_INCLUDED
|
||||
#define MINIUPNPCSTRINGS_H_INCLUDED
|
||||
|
||||
#define OS_STRING "${CMAKE_SYSTEM_NAME}"
|
||||
#define MINIUPNPC_VERSION_STRING "${PROJECT_VERSION}"
|
||||
|
||||
#if 0
|
||||
/* according to "UPnP Device Architecture 1.0" */
|
||||
#define UPNP_VERSION_MAJOR 1
|
||||
#define UPNP_VERSION_MINOR 0
|
||||
#define UPNP_VERSION_STRING "UPnP/1.0"
|
||||
#else
|
||||
/* according to "UPnP Device Architecture 1.1" */
|
||||
#define UPNP_VERSION_MAJOR 1
|
||||
#define UPNP_VERSION_MINOR 1
|
||||
#define UPNP_VERSION_STRING "UPnP/1.1"
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,27 @@
|
||||
/* $Id: miniupnpcstrings.h.in,v 1.7 2023/07/05 22:43:50 nanard Exp $ */
|
||||
/* Project: miniupnp
|
||||
* http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
|
||||
* Author: Thomas Bernard
|
||||
* Copyright (c) 2005-2014 Thomas Bernard
|
||||
* This software is subjects to the conditions detailed
|
||||
* in the LICENCE file provided within this distribution */
|
||||
#ifndef MINIUPNPCSTRINGS_H_INCLUDED
|
||||
#define MINIUPNPCSTRINGS_H_INCLUDED
|
||||
|
||||
#define OS_STRING "OS/version"
|
||||
#define MINIUPNPC_VERSION_STRING "version"
|
||||
|
||||
#if 0
|
||||
/* according to "UPnP Device Architecture 1.0" */
|
||||
#define UPNP_VERSION_MAJOR 1
|
||||
#define UPNP_VERSION_MINOR 0
|
||||
#define UPNP_VERSION_STRING "UPnP/1.0"
|
||||
#else
|
||||
/* according to "UPnP Device Architecture 1.1" */
|
||||
#define UPNP_VERSION_MAJOR 1
|
||||
#define UPNP_VERSION_MINOR 1
|
||||
#define UPNP_VERSION_STRING "UPnP/1.1"
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user