// PORT: in-client account registration. 40250 has no counterpart: its accounts were made on the web. // // net.RegisterAccount(host, port, login, password, socialId) starts one HTTP POST /register to the // server's registration service (tools/40250/register_server.py) on a worker thread; // net.GetRegisterAccountResult() polls it from the login window's OnUpdate. The reply is a single // line, "OK" or "ERR ", returned to Python as-is. #include "StdAfx.h" #include #include #include #include #include #include #include #include #include #include #include #include #include namespace { enum ERegisterState { REGISTER_IDLE, REGISTER_PENDING, REGISTER_DONE, }; const int c_iRegisterTimeoutMs = 8000; std::mutex gs_kRegisterMutex; std::atomic gs_iRegisterState(REGISTER_IDLE); std::string gs_strRegisterResult; std::string __UrlEncode(const std::string& c_rstText) { static const char c_szHex[] = "0123456789ABCDEF"; std::string stOut; for (unsigned char c : c_rstText) { if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || c == '-' || c == '_' || c == '.' || c == '~') { stOut += char(c); } else { stOut += '%'; stOut += c_szHex[c >> 4]; stOut += c_szHex[c & 15]; } } return stOut; } bool __WaitFor(int iSocket, short sEvents) { pollfd kPoll = { iSocket, sEvents, 0 }; return poll(&kPoll, 1, c_iRegisterTimeoutMs) == 1 && (kPoll.revents & sEvents); } std::string __Post(const std::string& c_rstHost, int iPort, const std::string& c_rstBody) { addrinfo kHints; memset(&kHints, 0, sizeof(kHints)); kHints.ai_family = AF_INET; kHints.ai_socktype = SOCK_STREAM; addrinfo* pkAddr = NULL; if (getaddrinfo(c_rstHost.c_str(), std::to_string(iPort).c_str(), &kHints, &pkAddr) != 0 || !pkAddr) return "ERR CONNECT"; const int iSocket = socket(pkAddr->ai_family, pkAddr->ai_socktype, pkAddr->ai_protocol); if (iSocket < 0) { freeaddrinfo(pkAddr); return "ERR CONNECT"; } #ifdef SO_NOSIGPIPE const int iOne = 1; setsockopt(iSocket, SOL_SOCKET, SO_NOSIGPIPE, &iOne, sizeof(iOne)); #endif fcntl(iSocket, F_SETFL, fcntl(iSocket, F_GETFL, 0) | O_NONBLOCK); std::string stResult = "ERR CONNECT"; int iError = 0; socklen_t iErrorLen = sizeof(iError); if (connect(iSocket, pkAddr->ai_addr, pkAddr->ai_addrlen) == 0 || (errno == EINPROGRESS && __WaitFor(iSocket, POLLOUT) && getsockopt(iSocket, SOL_SOCKET, SO_ERROR, &iError, &iErrorLen) == 0 && iError == 0)) { const std::string stRequest = "POST /register HTTP/1.0\r\n" "Host: " + c_rstHost + "\r\n" "Content-Type: application/x-www-form-urlencoded\r\n" "Content-Length: " + std::to_string(c_rstBody.size()) + "\r\n" "Connection: close\r\n\r\n" + c_rstBody; size_t uSent = 0; while (uSent < stRequest.size() && __WaitFor(iSocket, POLLOUT)) { #ifdef MSG_NOSIGNAL const ssize_t n = send(iSocket, stRequest.data() + uSent, stRequest.size() - uSent, MSG_NOSIGNAL); #else const ssize_t n = send(iSocket, stRequest.data() + uSent, stRequest.size() - uSent, 0); #endif if (n <= 0) break; uSent += size_t(n); } std::string stResponse; if (uSent == stRequest.size()) { char acBuf[512]; while (stResponse.size() < 4096 && __WaitFor(iSocket, POLLIN)) { const ssize_t n = recv(iSocket, acBuf, sizeof(acBuf), 0); if (n <= 0) break; stResponse.append(acBuf, size_t(n)); } } const size_t uBody = stResponse.find("\r\n\r\n"); if (uBody == std::string::npos) { stResult = uSent == stRequest.size() ? "ERR TIMEOUT" : "ERR CONNECT"; } else { stResult = stResponse.substr(uBody + 4); const size_t uEnd = stResult.find_first_of("\r\n"); if (uEnd != std::string::npos) stResult.resize(uEnd); if (stResult.empty()) stResult = "ERR SERVER"; } } close(iSocket); freeaddrinfo(pkAddr); return stResult; } } PyObject* netRegisterAccount(PyObject* poSelf, PyObject* poArgs) { char* szHost; int iPort; char* szLogin; char* szPassword; char* szSocialID; if (!PyTuple_GetString(poArgs, 0, &szHost)) return Py_BuildException(); if (!PyTuple_GetInteger(poArgs, 1, &iPort)) return Py_BuildException(); if (!PyTuple_GetString(poArgs, 2, &szLogin)) return Py_BuildException(); if (!PyTuple_GetString(poArgs, 3, &szPassword)) return Py_BuildException(); if (!PyTuple_GetString(poArgs, 4, &szSocialID)) return Py_BuildException(); int iExpected = REGISTER_IDLE; if (!gs_iRegisterState.compare_exchange_strong(iExpected, REGISTER_PENDING)) { iExpected = REGISTER_DONE; if (!gs_iRegisterState.compare_exchange_strong(iExpected, REGISTER_PENDING)) return Py_BuildValue("i", 0); // one request at a time } const std::string stHost = szHost; const std::string stBody = "login=" + __UrlEncode(szLogin) + "&password=" + __UrlEncode(szPassword) + "&social_id=" + __UrlEncode(szSocialID); std::thread([stHost, iPort, stBody]() { const std::string stResult = __Post(stHost, iPort, stBody); { std::lock_guard kLock(gs_kRegisterMutex); gs_strRegisterResult = stResult; } gs_iRegisterState = REGISTER_DONE; }).detach(); return Py_BuildValue("i", 1); } // Returns "" while the request runs (or none was started), else its result once. PyObject* netGetRegisterAccountResult(PyObject* poSelf, PyObject* poArgs) { int iExpected = REGISTER_DONE; if (!gs_iRegisterState.compare_exchange_strong(iExpected, REGISTER_IDLE)) return Py_BuildValue("s", ""); std::lock_guard kLock(gs_kRegisterMutex); return Py_BuildValue("s", gs_strRegisterResult.c_str()); }