7#if defined(HAVE_CONFIG_H)
47#define RESIPROCATE_SUBSYSTEM Subsystem::REPRO
52#define REPRO_BORDERLESS_TABLE_PROPS " class=\"form-table\""
53#define REPRO_BORDERED_TABLE_PROPS " class=\"data-table\""
60#define REPRO_DEFAULT_COMMAND_PORT 5081
68 "<svg class=\"logo\" viewBox=\"0 0 32 32\" role=\"img\" aria-label=\"Repro\">"
69 "<rect x=\"0.9\" y=\"0.9\" width=\"30.2\" height=\"30.2\" rx=\"6.5\""
70 " fill=\"var(--mark-navy)\" stroke=\"var(--mark-edge)\" stroke-width=\"1.6\"/>"
71 "<g stroke=\"var(--mark-amber)\" stroke-width=\"2.2\" fill=\"none\" stroke-linecap=\"round\">"
72 "<path d=\"M8 16h7\"/><path d=\"M15 16l8-6.5\"/><path d=\"M15 16l8 6.5\"/></g>"
73 "<circle cx=\"8\" cy=\"16\" r=\"3\" fill=\"var(--mark-amber)\"/>"
74 "<circle cx=\"23\" cy=\"9.5\" r=\"3\" fill=\"#ffb454\"/>"
75 "<circle cx=\"23\" cy=\"22.5\" r=\"3\" fill=\"#ffb454\"/>"
85 "<link rel=\"icon\" href=\"data:image/svg+xml,"
86 "<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'>"
87 "<rect width='32' height='32' rx='7' fill='%2312294d'/>"
88 "<g stroke='%23ff8d09' stroke-width='2.4' fill='none' stroke-linecap='round'>"
89 "<path d='M8 16h7'/><path d='M15 16l8-6.5'/><path d='M15 16l8 6.5'/></g>"
90 "<circle cx='8' cy='16' r='3' fill='%23ff8d09'/>"
91 "<circle cx='23' cy='9.5' r='3' fill='%23ffb454'/>"
92 "<circle cx='23' cy='22.5' r='3' fill='%23ffb454'/>"
104 " :root { --bg:#0a0c0f; --bg2:#10141a; --border:#1e2730; --green:#00e676;\n"
105 " --blue:#40c4ff; --text:#c8d0da; --text-dim:#4a5568; --text-mid:#7a8899;\n"
106 " --mark-navy:#12294d; --mark-edge:#2f5da8; --mark-amber:#ff8d09;\n"
107 " --mono:'IBM Plex Mono',ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;\n"
108 " color-scheme:dark; }\n"
109 " *,*::before,*::after { box-sizing:border-box; margin:0; padding:0; }\n"
110 " body { background:var(--bg); color:var(--text); font-family:var(--mono); font-size:13px;\n"
111 " line-height:1.55; min-height:100vh; display:flex; align-items:center;\n"
112 " justify-content:center; padding:24px; }\n"
113 " .card { background:var(--bg2); border:1px solid var(--border); border-radius:3px;\n"
114 " padding:26px 28px; max-width:520px; width:100%; }\n"
115 " .brand { display:flex; align-items:center; gap:12px; }\n"
116 " .logo { width:38px; height:38px; flex-shrink:0; display:block; }\n"
117 " .name { font-size:16px; font-weight:600; letter-spacing:.12em; text-transform:uppercase; }\n"
118 " .sub { font-size:10px; letter-spacing:.14em; text-transform:uppercase;\n"
119 " color:var(--text-dim); margin:6px 0 20px; }\n"
120 " .cta { display:inline-block; padding:8px 20px; border:1px solid var(--green);\n"
121 " border-radius:2px; color:var(--green); text-decoration:none; font-size:11px;\n"
122 " font-weight:600; letter-spacing:.12em; text-transform:uppercase;\n"
123 " transition:all .15s; }\n"
124 " .cta:hover { background:var(--green); color:var(--bg); }\n"
125 " p { color:var(--text-mid); margin-top:18px; font-size:12px; }\n"
126 " a { color:var(--blue); text-decoration:none; }\n"
127 " a:hover { text-decoration:underline; }\n"
131WebAdmin::RemoveKey::RemoveKey(
const Data &key1,
const Data &key2) : mKey1(key1), mKey2(key2)
136WebAdmin::RemoveKey::operator<(
const RemoveKey& rhs)
const
138 if(mKey1 < rhs.mKey1)
142 else if(mKey1 == rhs.mKey1 && mKey2 < rhs.mKey2)
159 HttpBase(port, version, realm, ipAddr),
161 mStore(*mProxy.getConfig().getDataStore()),
164 mNoWebChallenges(proxy.getConfig().getConfigBool(
"DisableHttpAuth", false)),
166#include
"repro/webadmin/pageOutlinePre.ixx"
169#include
"repro/webadmin/pageOutlinePost.ixx"
171 mUserFile(proxy.getConfig().getConfigData(
"HttpAdminUserFile",
"users.txt")),
205 InfoLog(<<
"Trying to load web admin users from: " << mUserFile);
206 std::ifstream users(mUserFile.
c_str());
211 throw ConfigException(
"Error opening/reading user database file", __FILE__, __LINE__);
213 std::map<Data,Data> userMap;
215 while(std::getline(users, sline))
238 const char * anchor = pb.
position();
244 ErrLog(<<
"Missing or invalid credentials at line " << lineNbr);
248 pb.
data(username, anchor);
262 ErrLog(<<
"Missing or invalid credentials at line " << lineNbr);
266 pb.
data(realm, anchor);
281 pb.
data(ha1, anchor);
286 DebugLog(<<
"Ignoring user " << username <<
" for realm " << realm);
290 userMap[username] = ha1;
293 InfoLog(<<
"Processed " << userMap.size() <<
" user(s) from " << lineNbr <<
" line(s) in " << mUserFile);
306 DebugLog (<<
"Parsing URL" << uri );
308 const char* anchor = pb.
skipChar(
'/');
311 pb.
data(pageName,anchor);
313 DebugLog (<<
" got page name: " << pageName );
318 bool isRestRequest = uri.
prefix(
"/api/v1");
321 if ( !isRestRequest &&
322 ( pageName !=
Data(
"index.html") ) &&
323 ( pageName !=
Data(
"input") ) &&
324 ( pageName !=
Data(
"cert.cer") ) &&
325 ( ! pageName.
prefix(
"cert") ) &&
326 ( pageName !=
Data(
"userTest.html") ) &&
327 ( pageName !=
Data(
"domains.html") ) &&
328 ( pageName !=
Data(
"acls.html") ) &&
329 ( pageName !=
Data(
"addUser.html") ) &&
330 ( pageName !=
Data(
"editUser.html") ) &&
331 ( pageName !=
Data(
"showUsers.html") ) &&
332 ( pageName !=
Data(
"addFilter.html") ) &&
333 ( pageName !=
Data(
"editFilter.html") ) &&
334 ( pageName !=
Data(
"showFilters.html") )&&
335 ( pageName !=
Data(
"addRoute.html") ) &&
336 ( pageName !=
Data(
"editRoute.html") ) &&
337 ( pageName !=
Data(
"showRoutes.html") )&&
338 ( pageName !=
Data(
"registrations.html") ) &&
339 ( pageName !=
Data(
"publications.html")) &&
340 ( pageName !=
Data(
"settings.html")) &&
341 ( pageName !=
Data(
"restart.html") ) &&
342 ( pageName !=
Data(
"logLevel.html") ) &&
343 ( pageName !=
Data(
"reloadcerts.html") ) &&
344 ( pageName !=
Data(
"user.html") ) )
351 if ( pageName ==
Data(
"index.html") )
353 setPage( buildDefaultPage(), pageNumber, 200);
358 if ( pageName.
prefix(
"cert") || pageName ==
Data(
"cert.cer") )
364 const char* anchor = pb.
skipChar(
'?');
369 if (
query ==
"domain" )
373 pb.
data(domain, anchor);
380 if ( !domain.
empty() )
382 InfoLog( <<
"domain is " << domain );
385 setPage( buildCertPage(domain), pageNumber, 200,
Mime(
"application",
"pkix-cert") );
404 Data authenticatedUser;
405 if (mNoWebChallenges)
408 authenticatedUser =
Data(
"admin");
425 std::map<Data,Data>::iterator it = mUsers.find(pUser);
426 if(it != mUsers.end())
441 if ( dbA1 == compA1 )
443 authenticatedUser = pUser;
447 InfoLog( <<
"user " << pUser <<
" failed to authenticate to web server" );
448 DebugLog( <<
" compA1="<<compA1<<
" dbA1="<<dbA1 );
455 setPage(
"User does not exist.", pageNumber,401 );
466 mRestAdmin->dispatch(method, uri, pageNumber, authenticatedUser);
480 const char* anchor1 = pb.
position();
483 pb.
data(key,anchor1);
485 const char* anchor2 = pb.
skipChar(
'=');
488 pb.
data(value,anchor2);
495 if ( key.
prefix(
"remove.") )
506 DebugLog (<<
" key=" << key <<
" value=" << value <<
" & unencoded form: " << value.
urlDecoded() );
512 DebugLog( <<
"building page for user=" << authenticatedUser );
515 if ( authenticatedUser ==
Data(
"admin") )
518 s << mPageOutlinePre;
521 if ( pageName ==
Data(
"user.html") ) {};
523 if ( pageName ==
Data(
"domains.html") ) buildDomainsSubPage(s);
524 if ( pageName ==
Data(
"acls.html") ) buildAclsSubPage(s);
526 if ( pageName ==
Data(
"addUser.html") ) buildAddUserSubPage(s);
527 if ( pageName ==
Data(
"editUser.html") ) buildEditUserSubPage(s);
528 if ( pageName ==
Data(
"showUsers.html") ) buildShowUsersSubPage(s);
530 if ( pageName ==
Data(
"addFilter.html") ) buildAddFilterSubPage(s);
531 if ( pageName ==
Data(
"editFilter.html") ) buildEditFilterSubPage(s);
532 if ( pageName ==
Data(
"showFilters.html") ) buildShowFiltersSubPage(s);
534 if ( pageName ==
Data(
"addRoute.html") ) buildAddRouteSubPage(s);
535 if ( pageName ==
Data(
"editRoute.html") ) buildEditRouteSubPage(s);
536 if ( pageName ==
Data(
"showRoutes.html") ) buildShowRoutesSubPage(s);
538 if ( pageName ==
Data(
"registrations.html")) buildRegistrationsSubPage(s);
539 if ( pageName ==
Data(
"publications.html")) buildPublicationsSubPage(s);
540 if ( pageName ==
Data(
"settings.html")) buildSettingsSubPage(s);
541 if ( pageName ==
Data(
"restart.html")) buildRestartSubPage(s);
542 if ( pageName ==
Data(
"logLevel.html")) buildLogLevelSubPage(s);
543 if ( pageName ==
Data(
"reloadcerts.html")) buildReloadCertsSubPage(s);
545 s << mPageOutlinePost;
548 if ( pageName ==
Data(
"userTest.html") ) page=buildUserPage();
550 else if ( !authenticatedUser.
empty() )
553 if ( pageName ==
Data(
"user.html") ) page=buildUserPage();
559 s <<
"Invalid page request";
561 setPage(page, pageNumber, 500);
569 setPage( page, pageNumber,200 );
577 if (!mRemoveSet.empty() && (mHttpParams[
"action"] ==
"Remove"))
580 for (set<RemoveKey>::iterator i = mRemoveSet.begin(); i != mRemoveSet.end(); ++i)
585 s <<
"<p class=\"notice ok\"><em>Removed:</em> " << j <<
" records</p>" << endl;
588 Dictionary::iterator pos = mHttpParams.find(
"domainUri");
589 if (pos != mHttpParams.end() && (mHttpParams[
"action"] ==
"Add"))
591 domainUri = pos->second;
598 s <<
"<p class=\"notice ok\"><em>Added</em> domain: " << domainUri <<
"</p>" << endl;
602 s <<
"<p class=\"notice err\"><em>Error</em> adding domain: likely database error (check logs).</p>\n";
607 " <h2>Domains</h2>" << endl <<
608 " <p>Requests addressed to these domains are treated as locally served, rather than" << endl <<
609 " being proxied onwards to another host.</p>" << endl <<
610 " <form id=\"domainForm\" method=\"get\" action=\"domains.html\" name=\"domainForm\">" << endl <<
613 " <td align=\"right\">New Domain:</td>" << endl <<
614 " <td><input type=\"text\" name=\"domainUri\" size=\"24\" placeholder=\"example.com\"/></td>" << endl <<
615 " <td><input type=\"submit\" name=\"action\" value=\"Add\"/></td>" << endl <<
617 " </table>" << endl <<
618 " <div class=space>" << endl <<
622 " <thead>" << endl <<
624 " <td>Domain</td>" << endl <<
625 " <td><input type=\"submit\" name=\"action\" value=\"Remove\"/></td>" << endl <<
627 " </thead>" << endl <<
631 for ( ConfigStore::ConfigData::const_iterator i = configs.begin();
632 i != configs.end(); i++ )
636 " <td>" << i->second.mDomain <<
"</td>" << endl <<
637 " <td><input type=\"checkbox\" name=\"remove." << i->second.mDomain <<
"\"/></td>" << endl <<
642 " </tbody>" << endl <<
643 " </table>" << endl <<
644 " </form>" << endl <<
645 "<p class=\"notice warn\"><em>WARNING:</em> You must restart repro after adding domains.</p>" << endl;
651 if (!mRemoveSet.empty() && (mHttpParams[
"action"] ==
"Remove"))
654 for (set<RemoveKey>::iterator i = mRemoveSet.begin(); i != mRemoveSet.end(); ++i)
659 s <<
"<p class=\"notice ok\"><em>Removed:</em> " << j <<
" records</p>" << endl;
662 Dictionary::iterator pos = mHttpParams.find(
"aclUri");
663 if (pos != mHttpParams.end() && (mHttpParams[
"action"] ==
"Add"))
665 Data hostOrIp = mHttpParams[
"aclUri"];
666 int port = mHttpParams[
"aclPort"].convertInt();
671 s <<
"<p class=\"notice ok\"><em>Added</em> trusted access for: " << hostOrIp <<
"</p>\n";
675 s <<
"<p class=\"notice err\"><em>Error</em> parsing: " << hostOrIp <<
"</p>\n";
680 " <h2>ACLs</h2>" << endl <<
681 " <div class=\"help\">" << endl <<
682 " <p>Access lists are used as a whitelist to allow gateways and other trusted" << endl <<
683 " nodes to skip authentication.</p>" << endl <<
684 " <p>If a hostname or FQDN is used then a TLS transport type is assumed. All" << endl <<
685 " other transport types must specify ACLs by address. Leave Port empty to match" << endl <<
686 " any port.</p>" << endl <<
687 " <pre>Host or IP can be in any of these formats" << endl <<
688 " localhost localhost (becomes 127.0.0.1/8, ::1/128 and fe80::1/64)" << endl <<
689 " bare hostname server1" << endl <<
690 " FQDN server1.example.com" << endl <<
691 " IPv4 address 192.168.1.100" << endl <<
692 " IPv4 + mask 192.168.1.0/24" << endl <<
693 " IPv6 address ::341:0:23:4bb:0011:2435:abcd" << endl <<
694 " IPv6 + mask ::341:0:23:4bb:0011:2435:abcd/80" << endl <<
695 " IPv6 reference [::341:0:23:4bb:0011:2435:abcd]" << endl <<
696 " IPv6 ref + mask [::341:0:23:4bb:0011:2435:abcd]/64</pre>" << endl <<
698 " <form id=\"aclsForm\" method=\"get\" action=\"acls.html\" name=\"aclsForm\">" << endl <<
701 " <td align=\"right\">Host or IP:</td>" << endl <<
702 " <td><input type=\"text\" name=\"aclUri\" size=\"24\" placeholder=\"host, FQDN or IP/mask\"/></td>" << endl <<
705 " <td><input type=\"text\" name=\"aclPort\" size=\"10\" placeholder=\"Port\"/></td>" << endl <<
706 " <td><select name=\"aclTransport\">" << endl <<
707 " <option selected=\"selected\">UDP</option>" << endl <<
708 " <option>TCP</option>" << endl <<
710 " <option>TLS</option>" << endl <<
713 " <option>DTLS</option>" << endl <<
715 " </select></td>" << endl <<
716 " <td><input type=\"submit\" name=\"action\" value=\"Add\"/></td>" << endl <<
718 " </table>" << endl <<
719 " <div class=space>" << endl <<
723 " <thead>" << endl <<
725 " <td>Host Address or Peer Name</td>" << endl <<
726 " <td>Port</td>" << endl <<
727 " <td>Transport</td>" << endl <<
728 " <td><input type=\"submit\" name=\"action\" value=\"Remove\"/></td>" << endl <<
730 " </thead>" << endl <<
739 " <td>TLS auth</td>" << endl <<
740 " <td><input type=\"checkbox\" name=\"remove." << key <<
"\"/></td>" << endl <<
754 " <td><input type=\"checkbox\" name=\"remove." << key <<
"\"/></td>" << endl <<
760 " </tbody>" << endl <<
761 " </table>" << endl <<
768 Dictionary::iterator pos;
771 pos = mHttpParams.find(
"user");
772 if (pos != mHttpParams.end())
775 Data domain = mHttpParams[
"domain"];
783 if(mStore.
mUserStore.
addUser(user,domain,domain,mHttpParams[
"password"],
true,mHttpParams[
"name"],mHttpParams[
"email"]))
785 s <<
"<p class=\"notice ok\"><em>Added:</em> " << user <<
"@" << domain <<
"</p>\n";
789 s <<
"<p class=\"notice err\"><em>Error</em> adding user: likely database error (check logs).</p>\n";
794 "<h2>Add User</h2>" << endl <<
795 "<div class=\"help\">" << endl <<
796 " <p>Full Name and Email are recorded for display purposes only. They are not used" << endl <<
797 " for authentication, routing, or as a SIP identity.</p>" << endl <<
799 "<form id=\"addUserForm\" action=\"addUser.html\" method=\"get\" name=\"addUserForm\" enctype=\"application/x-www-form-urlencoded\">" << endl <<
802 " <td align=\"right\" valign=\"middle\">User Name:</td>" << endl <<
803 " <td align=\"left\" valign=\"middle\"><input type=\"text\" name=\"user\" size=\"40\" placeholder=\"alice\"/></td>" << endl <<
812 " <td align=\"right\" valign=\"middle\" >Domain:</td>" << endl <<
813 " <td align=\"left\" valign=\"middle\"><select name=\"domain\">" << endl
818 for ( ConfigStore::ConfigData::const_iterator i =
list.begin();
819 i !=
list.end(); i++ )
828 s <<
">" << i->second.mDomain <<
"</option>" << endl;
832 "</select></td></tr>" << endl <<
834 " <td align=\"right\" valign=\"middle\" >Password:</td>" << endl <<
835 " <td align=\"left\" valign=\"middle\"><input type=\"password\" name=\"password\" size=\"40\" placeholder=\"account password\"/></td>" << endl <<
839 " <td align=\"right\" valign=\"middle\" >Full Name:</td>" << endl <<
840 " <td align=\"left\" valign=\"middle\"><input type=\"text\" name=\"name\" size=\"40\" placeholder=\"Alice Smith\"/></td>" << endl <<
844 " <td align=\"right\" valign=\"middle\" >Email:</td>" << endl <<
845 " <td align=\"left\" valign=\"middle\"><input type=\"text\" name=\"email\" size=\"40\" placeholder=\"alice@example.com\"/></td>" << endl <<
849 " <td colspan=\"2\" align=\"right\" valign=\"middle\">" << endl <<
850 " <input type=\"reset\" value=\"Cancel\"/>" << endl <<
851 " <input type=\"submit\" name=\"submit\" value=\"Add\"/>" << endl <<
855 "</table>" << endl <<
863 Dictionary::iterator pos;
864 pos = mHttpParams.find(
"key");
865 if (pos != mHttpParams.end())
867 Data key = pos->second;
871 s <<
"<h2>Edit User</h2>" << endl <<
872 "<div class=\"help\">" << endl <<
873 " <p>Editing record with key: " << key <<
"</p>" << endl <<
874 " <p>If you leave the password field empty, the user's current "
875 "password will not be reset. However, if you change the username or "
876 "domain, a new password is required (the stored password hash is "
877 "bound to user+realm, so renaming without a new password would "
878 "invalidate the account).</p>" << endl <<
879 " <p>Full Name and Email are recorded for display purposes only. They are not used "
880 "for authentication, routing, or as a SIP identity.</p>" << endl <<
884 "<form id=\"editUserForm\" action=\"showUsers.html\" method=\"get\" name=\"editUserForm\" enctype=\"application/x-www-form-urlencoded\">" << endl <<
886 "<input type=\"hidden\" name=\"key\" value=\"" << key <<
"\"/>" << endl <<
888 " <td align=\"right\" valign=\"middle\">User Name:</td>" << endl <<
889 " <td align=\"left\" valign=\"middle\"><input type=\"text\" name=\"user\" placeholder=\"alice\" value=\"" << rec.
user <<
"\" size=\"40\"/></td>" << endl <<
898 " <td align=\"right\" valign=\"middle\" >Domain:</td>" << endl <<
899 " <td align=\"left\" valign=\"middle\"><select name=\"domain\">" << endl
904 for ( ConfigStore::ConfigData::const_iterator i =
list.begin();
905 i !=
list.end(); i++ )
909 if ( i->second.mDomain == rec.
domain)
911 s <<
" selected=\"true\"";
914 s <<
">" << i->second.mDomain <<
"</option>" << endl;
918 "</select></td></tr>" << endl <<
920 " <td align=\"right\" valign=\"middle\" >Password:</td>" << endl <<
921 " <td align=\"left\" valign=\"middle\"><input type=\"password\" name=\"password\" size=\"40\" placeholder=\"leave empty to keep current password\"/></td>" << endl <<
927 " <td align=\"right\" valign=\"middle\" >Full Name:</td>" << endl <<
928 " <td align=\"left\" valign=\"middle\"><input type=\"text\" name=\"name\" value=\"" << rec.
name <<
929 "\" size=\"40\" placeholder=\"Alice Smith\"/></td>" << endl <<
933 " <td align=\"right\" valign=\"middle\" >Email:</td>" << endl <<
934 " <td align=\"left\" valign=\"middle\"><input type=\"text\" name=\"email\" value=\"" << rec.
email <<
935 "\" size=\"40\" placeholder=\"alice@example.com\"/></td>" << endl <<
939 " <td colspan=\"2\" align=\"right\" valign=\"middle\">" << endl <<
940 " <input type=\"submit\" name=\"submit\" value=\"Update\"/>" << endl <<
944 "</table>" << endl <<
957 Dictionary::iterator pos;
961 if (!mRemoveSet.empty())
964 for (set<RemoveKey>::iterator i = mRemoveSet.begin(); i != mRemoveSet.end(); ++i)
969 s <<
"<p class=\"notice ok\"><em>Removed:</em> " << j <<
" records</p>" << endl;
972 pos = mHttpParams.find(
"key");
973 if (pos != mHttpParams.end())
980 Data user = mHttpParams[
"user"];
981 Data domain = mHttpParams[
"domain"];
982 Data realm = mHttpParams[
"domain"];
983 Data password = mHttpParams[
"password"];
985 Data name = mHttpParams[
"name"];
986 Data email = mHttpParams[
"email"];
987 bool applyA1HashToPassword =
true;
992 bool identityChanged = (user != rec.
user) || (realm != rec.
realm);
993 if (identityChanged && password.
empty())
995 s <<
"<p class=\"notice err\"><em>Error</em> updating user: changing user or domain "
996 "requires a new password (the stored password hash is bound "
997 "to user+realm).</p>\n";
1003 if (password.
empty() && !identityChanged)
1007 applyA1HashToPassword =
false;
1010 if(mStore.
mUserStore.
updateUser(key, user, domain, realm, password, applyA1HashToPassword, name, email, passwordHashAlt))
1012 s <<
"<p class=\"notice ok\"><em>Updated:</em> " << key <<
"</p>" << endl;
1016 s <<
"<p class=\"notice err\"><em>Error</em> updating user: likely database error (check logs).</p>\n";
1023 "<h2>Users</h2>" << endl <<
1024 "<form id=\"showUsers\" method=\"get\" action=\"showUsers.html\" name=\"showUsers\" enctype=\"application/x-www-form-urlencoded\">" << endl <<
1026 "<thead><tr>" << endl <<
1027 " <td>User@Domain</td>" << endl <<
1029 " <td>Name</td>" << endl <<
1030 " <td>Email</td>" << endl <<
1031 " <td><input type=\"submit\" value=\"Remove\"/></td>" << endl <<
1032 "</tr></thead>" << endl <<
1040 while ( !key.
empty() )
1045 <<
" <td><a href=\"editUser.html?key=";
1047 s <<
"\">" << rec.
user <<
"@" << rec.
domain <<
"</a></td>" << endl
1048 <<
" <td>" << rec.
name <<
"</td>" << endl
1049 <<
" <td>" << rec.
email <<
"</td>" << endl
1050 <<
" <td><input type=\"checkbox\" name=\"remove." << key <<
"\"/></td>" << endl
1056 if ( ++count > 1000 )
1064 s <<
"<tr><td colspan=\"4\">Only first 1000 users were displayed</td></tr>" << endl;
1068 "</tbody>" << endl <<
1069 "</table>" << endl <<
1074WebAdmin::buildAddFilterSubPage(
DataStream& s)
1076 Dictionary::iterator pos;
1078 pos = mHttpParams.find(
"cond1header");
1079 if (pos != mHttpParams.end())
1081 Data action = mHttpParams[
"action"];
1082 Data actionData = mHttpParams[
"actiondata"];
1084 if (action !=
"Accept" && actionData.
empty())
1086 s <<
"<p class=\"notice err\"><em>Error</em> adding request filter. You must provide appropriate Action Data for non-Accept action.</p>\n";
1090 short actionShort = 0;
1091 if(action ==
"Reject") actionShort = 1;
1092 else if(action ==
"SQL Query") actionShort = 2;
1095 mHttpParams[
"cond1regex"],
1096 mHttpParams[
"cond2header"],
1097 mHttpParams[
"cond2regex"],
1098 mHttpParams[
"method"],
1099 mHttpParams[
"event"],
1102 mHttpParams[
"order"].convertInt()))
1104 s <<
"<p class=\"notice ok\"><em>Added</em> request filter: " << mHttpParams[
"cond1header"] <<
"=" << mHttpParams[
"cond1regex"] <<
", "
1105 << mHttpParams[
"cond2header"] <<
"=" << mHttpParams[
"cond2regex"] <<
"</p>\n";
1109 s <<
"<p class=\"notice err\"><em>Error</em> adding request filter, likely duplicate found.</p>\n";
1115 "<h2>Add Request Filter</h2>" << endl <<
1116 "<div class=\"help\">" << endl <<
1117 " <p>A request filter matches an incoming request against one or two header values" << endl <<
1118 " and decides whether to accept or reject it. Both conditions must match for the" << endl <<
1119 " filter to fire; leave the second pair empty to match on one header alone. Regexes" << endl <<
1120 " are POSIX-standard and are matched against the header's value.</p>" << endl <<
1121 " <p>Method and Event narrow the filter to a single request type; leave them empty to" << endl <<
1122 " match any. Order decides evaluation sequence when several filters could match --" << endl <<
1123 " lowest first.</p>" << endl <<
1124 " <p>Action Data depends on the chosen Action:</p>" << endl <<
1125 " <pre>Accept Action Data is ignored." << endl <<
1126 "Reject SIPRejectionCode[, SIPReason] e.g. 403, Request Blocked" << endl <<
1128 "SQL Query The SQL query to execute. Replacement strings from the regexes" << endl <<
1129 " above can be used in the query. The query must return a string" << endl <<
1130 " formatted like the Reject data above, or a string with a status" << endl <<
1131 " code of 0 to accept the request." << endl <<
1135 "<form id=\"addFilterForm\" method=\"get\" action=\"addFilter.html\" name=\"addFilterForm\">" << endl <<
1139 " <td align=\"right\" valign=\"middle\">Condition1 Header:</td>" << endl <<
1140 " <td align=\"left\" valign=\"middle\"><input type=\"text\" name=\"cond1header\" size=\"40\" value=\"From\" placeholder=\"header name, e.g. From\"/></td>" << endl <<
1144 " <td align=\"right\" valign=\"middle\">Condition1 Regex:</td>" << endl <<
1145 " <td align=\"left\" valign=\"middle\"><input type=\"text\" name=\"cond1regex\" size=\"40\" placeholder=\"POSIX regex, e.g. sip:1234@.*\"/></td>" << endl <<
1149 " <td align=\"right\" valign=\"middle\">Condition2 Header:</td>" << endl <<
1150 " <td align=\"left\" valign=\"middle\"><input type=\"text\" name=\"cond2header\" size=\"40\" value=\"To\" placeholder=\"header name, e.g. To\"/></td>" << endl <<
1154 " <td align=\"right\" valign=\"middle\">Condition2 Regex:</td>" << endl <<
1155 " <td align=\"left\" valign=\"middle\"><input type=\"text\" name=\"cond2regex\" size=\"40\" placeholder=\"POSIX regex, empty = any\"/></td>" << endl <<
1159 " <td align=\"right\" valign=\"middle\">Method:</td>" << endl <<
1160 " <td align=\"left\" valign=\"middle\"><input type=\"text\" name=\"method\" size=\"40\" placeholder=\"INVITE, REGISTER... empty = any\"/></td>" << endl <<
1164 " <td align=\"right\" valign=\"middle\">Event:</td>" << endl <<
1165 " <td align=\"left\" valign=\"middle\"><input type=\"text\" name=\"event\" size=\"40\" placeholder=\"presence, message-summary... empty = any\"/></td>" << endl <<
1169 " <td align=\"right\" valign=\"middle\">Action:</td>" << endl <<
1170 " <td align=\"left\" valign=\"middle\">" << endl <<
1171 " <select name=\"action\">" << endl <<
1172 " <option>Reject</option>" << endl <<
1173 " <option>Accept</option>" << endl <<
1175 " <option>SQL Query</option>" << endl <<
1177 " </select>" << endl <<
1182 " <td align=\"right\" valign=\"middle\">Action Data:</td>" << endl <<
1183 " <td align=\"left\" valign=\"middle\"><input type=\"text\" name=\"actiondata\" size=\"40\" value=\"403, Request Blocked\" placeholder=\"e.g. 403, Request Blocked\"/></td>" << endl <<
1187 " <td align=\"right\" valign=\"middle\">Order:</td>" << endl <<
1188 " <td align=\"left\" valign=\"middle\"><input type=\"text\" name=\"order\" size=\"8\" value=\"0\" placeholder=\"0\"/></td>" << endl <<
1192 " <td colspan=\"2\" align=\"right\" valign=\"middle\">" << endl <<
1193 " <input type=\"reset\" value=\"Cancel\"/>" << endl <<
1194 " <input type=\"submit\" name=\"filterAdd\" value=\"Add\"/>" << endl <<
1198 "</table>" << endl <<
1203WebAdmin::buildEditFilterSubPage(
DataStream& s)
1205 Dictionary::iterator pos;
1206 pos = mHttpParams.find(
"key");
1207 if (pos != mHttpParams.end())
1209 Data key = pos->second;
1212 DebugLog( <<
"Creating page to edit filter " << key );
1216 s <<
"<h2>Edit Request Filter</h2>" << endl <<
1221 "<form id=\"editFilterForm\" method=\"get\" action=\"showFilters.html\" name=\"editFilterForm\">" << endl <<
1223 "<input type=\"hidden\" name=\"key\" value=\"" << key <<
"\"/>" << endl <<
1227 " <td align=\"right\" valign=\"middle\">Condition1 Header:</td>" << endl <<
1228 " <td align=\"left\" valign=\"middle\"><input type=\"text\" name=\"cond1header\" size=\"40\" placeholder=\"header name, e.g. From\" value=\"" << rec.
mCondition1Header.
xmlCharDataEncode() <<
"\"/></td>" << endl <<
1232 " <td align=\"right\" valign=\"middle\">Condition1 Regex:</td>" << endl <<
1233 " <td align=\"left\" valign=\"middle\"><input type=\"text\" name=\"cond1regex\" size=\"40\" placeholder=\"POSIX regex, e.g. sip:1234@.*\" value=\"" << rec.
mCondition1Regex.
xmlCharDataEncode() <<
"\"/></td>" << endl <<
1237 " <td align=\"right\" valign=\"middle\">Condition2 Header:</td>" << endl <<
1238 " <td align=\"left\" valign=\"middle\"><input type=\"text\" name=\"cond2header\" size=\"40\" placeholder=\"header name, e.g. To\" value=\"" << rec.
mCondition2Header.
xmlCharDataEncode() <<
"\"/></td>" << endl <<
1242 " <td align=\"right\" valign=\"middle\">Condition2 Regex:</td>" << endl <<
1243 " <td align=\"left\" valign=\"middle\"><input type=\"text\" name=\"cond2regex\" size=\"40\" placeholder=\"POSIX regex, empty = any\" value=\"" << rec.
mCondition2Regex.
xmlCharDataEncode() <<
"\"/></td>" << endl <<
1247 " <td align=\"right\" valign=\"middle\">Method:</td>" << endl <<
1248 " <td align=\"left\" valign=\"middle\"><input type=\"text\" name=\"method\" size=\"40\" placeholder=\"INVITE, REGISTER... empty = any\" value=\"" << rec.
mMethod <<
"\"/></td>" << endl <<
1252 " <td align=\"right\" valign=\"middle\">Event:</td>" << endl <<
1253 " <td align=\"left\" valign=\"middle\"><input type=\"text\" name=\"event\" size=\"40\" placeholder=\"presence, message-summary... empty = any\" value=\"" << rec.
mEvent <<
"\"/></td>" << endl <<
1257 " <td align=\"right\" valign=\"middle\">Action:</td>" << endl <<
1258 " <td align=\"left\" valign=\"middle\">" << endl <<
1259 " <select name=\"action\">" << endl <<
1263 " <option" << (rec.mAction ==
FilterStore::SQLQuery ?
" selected=\"selected\"" :
"") <<
">SQL Query</option>" << endl <<
1265 " </select>" << endl <<
1270 " <td align=\"right\" valign=\"middle\">Action Data:</td>" << endl <<
1271 " <td align=\"left\" valign=\"middle\"><input type=\"text\" name=\"actiondata\" size=\"40\" placeholder=\"e.g. 403, Request Blocked\" value=\"" << rec.mActionData.xmlCharDataEncode() <<
"\"/></td>" << endl <<
1275 " <td align=\"right\" valign=\"middle\">Order:</td>" << endl <<
1276 " <td align=\"left\" valign=\"middle\"><input type=\"text\" name=\"order\" size=\"8\" placeholder=\"0\" value=\"" << rec.mOrder <<
"\"/></td>" << endl <<
1280 " <td colspan=\"2\" align=\"right\" valign=\"middle\">" << endl <<
1281 " <input type=\"submit\" name=\"routeEdit\" value=\"Update\"/>" << endl <<
1285 "</table>" << endl <<
1295WebAdmin::buildShowFiltersSubPage(
DataStream& s)
1297 Dictionary::iterator pos;
1301 if (!mRemoveSet.empty())
1304 for (set<RemoveKey>::iterator i = mRemoveSet.begin(); i != mRemoveSet.end(); ++i)
1309 s <<
"<p class=\"notice ok\"><em>Removed:</em> " << j <<
" records</p>" << endl;
1312 pos = mHttpParams.find(
"key");
1313 if (pos != mHttpParams.end())
1320 Data action = mHttpParams[
"action"];
1321 Data actionData = mHttpParams[
"actiondata"];
1323 if (action !=
"Accept" && actionData.
empty())
1325 s <<
"<p class=\"notice err\"><em>Error</em> updating request filter. You must provide appropriate Action Data for non-Accept action.</p>\n";
1329 short actionShort = 0;
1330 if(action ==
"Reject") actionShort = 1;
1331 else if(action ==
"SQL Query") actionShort = 2;
1334 mHttpParams[
"cond1header"],
1335 mHttpParams[
"cond1regex"],
1336 mHttpParams[
"cond2header"],
1337 mHttpParams[
"cond2regex"],
1338 mHttpParams[
"method"],
1339 mHttpParams[
"event"],
1342 mHttpParams[
"order"].convertInt()))
1344 s <<
"<p class=\"notice ok\"><em>Updated</em> request filter: " << mHttpParams[
"cond1header"] <<
"=" << mHttpParams[
"cond1regex"] <<
", "
1345 << mHttpParams[
"cond2header"] <<
"=" << mHttpParams[
"cond2regex"] <<
"</p>\n";
1349 s <<
"<p class=\"notice err\"><em>Error</em> updating request filter: likely database error (check logs).</p>\n";
1356 "<h2>Request Filters</h2>" << endl <<
1357 "<form id=\"showFilters\" action=\"showFilters.html\" method=\"get\" name=\"showFilters\" enctype=\"application/x-www-form-urlencoded\">" << endl <<
1360 "<thead><tr>" << endl <<
1361 " <td>Condition 1</td>" << endl <<
1362 " <td>Condition 2</td>" << endl <<
1363 " <td>Method</td>" << endl <<
1364 " <td>Event</td>" << endl <<
1365 " <td>Action</td>" << endl <<
1366 " <td>Action Data</td>" << endl <<
1367 " <td>Order</td>" << endl <<
1368 " <td><input type=\"submit\" value=\"Remove\"/></td>" << endl <<
1369 "</tr></thead>" << endl <<
1377 Data action(
"Accept");
1384 action =
"SQL Query";
1386 s <<
"<tr>" << endl <<
1387 "<td><a href=\"editFilter.html?key=";
1392 "<td>" << rec.
mMethod <<
"</td>" << endl <<
1393 "<td>" << rec.
mEvent <<
"</td>" << endl <<
1394 "<td>" << action <<
"</td>" << endl <<
1396 "<td>" << rec.
mOrder <<
"</td>" << endl <<
1397 "<td><input type=\"checkbox\" name=\"remove." << key <<
"\"/></td>" << endl <<
1402 "</tbody>" << endl <<
1403 "</table>" << endl <<
1406 Data cond1TestHeader;
1407 pos = mHttpParams.find(
"cond1TestHeader");
1408 if (pos != mHttpParams.end())
1410 cond1TestHeader = pos->second;
1412 Data cond2TestHeader;
1413 pos = mHttpParams.find(
"cond2TestHeader");
1414 if (pos != mHttpParams.end())
1416 cond2TestHeader = pos->second;
1420 "<h3>Test Filters</h3>" << endl <<
1421 "<form id=\"testFilter\" action=\"showFilters.html\" method=\"get\" name=\"testFilter\" enctype=\"application/x-www-form-urlencoded\">" << endl <<
1424 " <td align=\"right\">Condition 1 Header:</td>" << endl <<
1425 " <td><input type=\"text\" name=\"cond1TestHeader\" placeholder=\"header value to match, e.g. sip:1234@example.com\" value=\"" << cond1TestHeader.
xmlCharDataEncode() <<
"\" size=\"40\"/></td>" << endl <<
1428 " <td align=\"right\">Condition 2 Header:</td>" << endl <<
1429 " <td><input type=\"text\" name=\"cond2TestHeader\" placeholder=\"header value to match, empty if unused\" value=\"" << cond2TestHeader.
xmlCharDataEncode() <<
"\" size=\"40\"/></td>" << endl <<
1430 " <td><input type=\"submit\" name=\"testFilter\" value=\"Test Filters\"/></td>" << endl <<
1432 "</table>" << endl <<
1435 if(!cond1TestHeader.
empty())
1437 s <<
"<p class=\"notice\"><em>Test Result:</em> ";
1440 if(mStore.
mFilterStore.
test(cond1TestHeader, cond2TestHeader, action, actionData))
1445 s <<
"Match found, action=Reject " << actionData << endl;
1448 s <<
"Match found, action=SQL Query '" << actionData <<
"'" << endl;
1452 s <<
"Match found, action=Accept" << endl;
1458 s <<
"<em class=\"warn\">No matches</em>";
1460 s <<
"</p>" << endl;
1467 Dictionary::iterator pos;
1469 pos = mHttpParams.find(
"routeUri");
1470 if (pos != mHttpParams.end())
1472 Data routeUri = mHttpParams[
"routeUri"];
1473 Data routeDestination = mHttpParams[
"routeDestination"];
1475 if (!routeUri.
empty() && !routeDestination.
empty())
1478 mHttpParams[
"routeEvent"],
1481 mHttpParams[
"routeOrder"].convertInt()))
1483 s <<
"<p class=\"notice ok\"><em>Added</em> route for: " << routeUri <<
"</p>\n";
1487 s <<
"<p class=\"notice err\"><em>Error</em> adding route, likely duplicate found.</p>\n";
1492 s <<
"<p class=\"notice err\"><em>Error</em> adding route. You must provide a URI and a route destination.</p>\n";
1497 "<h2>Add Route</h2>" << endl <<
1498 "<div class=\"help\">" << endl <<
1499 " <p>Static routes use (POSIX-standard) regular expressions to match and rewrite SIP" << endl <<
1500 " URIs. Method and Event narrow the route to a single request type; leave them empty" << endl <<
1501 " to match any. Order decides evaluation sequence when several routes could match --" << endl <<
1502 " lowest first.</p>" << endl <<
1503 " <p>The following sends all requests whose userpart is only digits to a gateway:</p>" << endl <<
1504 " <pre>URI: ^sip:([0-9]+)@example\\.com" << endl <<
1505 "Destination: sip:$1@gateway.example.com</pre>" << endl <<
1507 "<form id=\"addRouteForm\" method=\"get\" action=\"addRoute.html\" name=\"addRouteForm\">" << endl <<
1511 " <td align=\"right\" valign=\"middle\">URI:</td>" << endl <<
1512 " <td align=\"left\" valign=\"middle\"><input type=\"text\" name=\"routeUri\" size=\"40\" placeholder=\"^sip:([0-9]+)@example.com\"/></td>" << endl <<
1516 " <td align=\"right\" valign=\"middle\">Method:</td>" << endl <<
1517 " <td align=\"left\" valign=\"middle\"><input type=\"text\" name=\"routeMethod\" size=\"40\" placeholder=\"INVITE, REGISTER... empty = any\"/></td>" << endl <<
1521 " <td align=\"right\" valign=\"middle\">Event:</td>" << endl <<
1522 " <td align=\"left\" valign=\"middle\"><input type=\"text\" name=\"routeEvent\" size=\"40\" placeholder=\"presence, message-summary... empty = any\"/></td>" << endl <<
1526 " <td align=\"right\" valign=\"middle\">Destination:</td>" << endl <<
1527 " <td align=\"left\" valign=\"middle\"><input type=\"text\" name=\"routeDestination\" size=\"40\" placeholder=\"sip:$1@gateway.example.com\"/></td>" << endl <<
1531 " <td align=\"right\" valign=\"middle\">Order:</td>" << endl <<
1532 " <td align=\"left\" valign=\"middle\"><input type=\"text\" name=\"routeOrder\" size=\"8\" placeholder=\"0\"/></td>" << endl <<
1536 " <td colspan=\"2\" align=\"right\" valign=\"middle\">" << endl <<
1537 " <input type=\"reset\" value=\"Cancel\"/>" << endl <<
1538 " <input type=\"submit\" name=\"routeAdd\" value=\"Add\"/>" << endl <<
1542 "</table>" << endl <<
1547WebAdmin::buildEditRouteSubPage(
DataStream& s)
1549 Dictionary::iterator pos;
1550 pos = mHttpParams.find(
"key");
1551 if (pos != mHttpParams.end())
1553 Data key = pos->second;
1556 DebugLog( <<
"Creating page to edit route " << key );
1560 s <<
"<h2>Edit Route</h2>" << endl <<
1561 "<p>Editing Record with matching pattern: " << rec.
mMatchingPattern <<
"</p>" << endl;
1564 "<form id=\"editRouteForm\" method=\"get\" action=\"showRoutes.html\" name=\"editRouteForm\">" << endl <<
1566 "<input type=\"hidden\" name=\"key\" value=\"" << key <<
"\"/>" << endl <<
1568 "<td align=\"right\" valign=\"middle\">URI:</td>" << endl <<
1569 "<td align=\"left\" valign=\"middle\"><input type=\"text\" name=\"routeUri\" placeholder=\"^sip:([0-9]+)@example.com\" value=\"" << rec.
mMatchingPattern <<
"\" size=\"40\"/></td>" << endl <<
1573 "<td align=\"right\" valign=\"middle\">Method:</td>" << endl <<
1574 "<td align=\"left\" valign=\"middle\"><input type=\"text\" name=\"routeMethod\" placeholder=\"INVITE, REGISTER... empty = any\" value=\"" << rec.
mMethod <<
"\" size=\"40\"/></td>" << endl <<
1578 "<td align=\"right\" valign=\"middle\">Event:</td>" << endl <<
1579 "<td align=\"left\" valign=\"middle\"><input type=\"text\" name=\"routeEvent\" placeholder=\"presence, message-summary... empty = any\" value=\"" << rec.
mEvent <<
"\" size=\"40\"/></td>" << endl <<
1583 "<td align=\"right\" valign=\"middle\">Destination:</td>" << endl <<
1584 "<td align=\"left\" valign=\"middle\"><input type=\"text\" name=\"routeDestination\" placeholder=\"sip:$1@gateway.example.com\" value=\"" << rec.
mRewriteExpression <<
1585 "\" size=\"40\"/></td>" << endl <<
1589 "<td align=\"right\" valign=\"middle\">Order:</td>" << endl <<
1590 "<td align=\"left\" valign=\"middle\"><input type=\"text\" name=\"routeOrder\" placeholder=\"0\" value=\"" << rec.
mOrder <<
1591 "\" size=\"4\"/></td>" << endl <<
1595 " <td colspan=\"2\" align=\"right\" valign=\"middle\">" << endl <<
1596 " <input type=\"submit\" name=\"routeEdit\" value=\"Update\"/>" << endl <<
1600 "</table>" << endl <<
1610WebAdmin::buildShowRoutesSubPage(
DataStream& s)
1612 Dictionary::iterator pos;
1616 if (!mRemoveSet.empty())
1619 for (set<RemoveKey>::iterator i = mRemoveSet.begin(); i != mRemoveSet.end(); ++i)
1624 s <<
"<p class=\"notice ok\"><em>Removed:</em> " << j <<
" records</p>" << endl;
1627 pos = mHttpParams.find(
"key");
1628 if (pos != mHttpParams.end())
1635 Data method = mHttpParams[
"routeMethod"];
1636 Data event = mHttpParams[
"routeEvent"];
1637 Data matchingPattern = mHttpParams[
"routeUri"];
1638 Data rewriteExpression = mHttpParams[
"routeDestination"];
1639 int order = mHttpParams[
"routeOrder"].convertInt();
1641 if (!matchingPattern.
empty() && !rewriteExpression.
empty())
1646 s <<
"<p class=\"notice ok\"><em>Updated:</em> " << rec.
mMatchingPattern <<
"</p>" << endl;
1650 s <<
"<p class=\"notice err\"><em>Error</em> updating route: likely database error (check logs).</p>\n";
1655 s <<
"<p class=\"notice err\"><em>Error</em> updating route. You must provide a URI and a route destination.</p>\n";
1661 "<h2>Routes</h2>" << endl <<
1662 "<form id=\"showRoutes\" action=\"showRoutes.html\" method=\"get\" name=\"showRoutes\" enctype=\"application/x-www-form-urlencoded\">" << endl <<
1665 "<thead><tr>" << endl <<
1666 " <td>URI</td>" << endl <<
1667 " <td>Method</td>" << endl <<
1668 " <td>Event</td>" << endl <<
1669 " <td>Destination</td>" << endl <<
1670 " <td>Order</td>" << endl <<
1671 " <td><input type=\"submit\" value=\"Remove\"/></td>" << endl <<
1672 "</tr></thead>" << endl <<
1681 s <<
"<tr>" << endl <<
1682 "<td><a href=\"editRoute.html?key=";
1686 "<td>" << rec.
mMethod <<
"</td>" << endl <<
1687 "<td>" << rec.
mEvent <<
"</td>" << endl <<
1689 "<td>" << rec.
mOrder <<
"</td>" << endl <<
1690 "<td><input type=\"checkbox\" name=\"remove." << key <<
"\"/></td>" << endl <<
1695 "</tbody>" << endl <<
1696 "</table>" << endl <<
1703 pos = mHttpParams.find(
"routeTestUri");
1704 if (pos != mHttpParams.end())
1706 routeTestUri = pos->second;
1707 if ( routeTestUri !=
"sip:" )
1711 uri =
Uri(routeTestUri);
1718 uri =
Uri(
Data(
"sip:")+routeTestUri );
1736 "<h3>Test Routes</h3>" << endl <<
1737 "<form id=\"testRoute\" action=\"showRoutes.html\" method=\"get\" name=\"testRoute\" enctype=\"application/x-www-form-urlencoded\">" << endl <<
1740 " <td align=\"right\">Input:</td>" << endl <<
1741 " <td><input type=\"text\" name=\"routeTestUri\" placeholder=\"sip:1234@example.com\" value=\"" << uri <<
"\" size=\"40\"/></td>" << endl <<
1742 " <td><input type=\"submit\" name=\"testRoute\" value=\"Test Routes\"/></td>" << endl <<
1746 for ( RouteStore::UriList::const_iterator i=routeList.begin();
1747 i != routeList.end(); i++)
1753 s<<
" <td align=\"right\">Targets:</td>" << endl;
1757 s<<
" <td align=\"right\"></td>" << endl;
1759 s<<
" <td><label>" << *i <<
"</label></td>" << endl;
1760 s<<
" <td></td>" << endl;
1761 s<<
" </tr>" << endl;
1766 if (first && !routeTestUri.
empty())
1769 s<<
" <td align=\"right\">Targets:</td>" << endl;
1770 s<<
" <td><em class=\"warn\">"
1771 << (badUri ?
"Not a valid SIP URI" :
"No matches")
1772 <<
"</em></td>" << endl;
1773 s<<
" <td></td>" << endl;
1774 s<<
" </tr>" << endl;
1779 "</table>" << endl <<
1784WebAdmin::buildRegistrationsSubPage(
DataStream& s)
1786 if (!mRemoveSet.empty())
1789 for (set<RemoveKey>::iterator i = mRemoveSet.begin(); i != mRemoveSet.end(); ++i)
1799 WarningLog(<<
"Registration removal key was malformed: " << i->mKey2);
1803 bool staticRegContact=
false;
1810 staticRegContact = i->mKey2.substr(bar3+1,
Data::npos).convertInt() == 1;
1815 if(staticRegContact)
1823 catch(resip::ParseBuffer::Exception& e)
1825 WarningLog(<<
"Registration removal key was malformed: " << e <<
1826 " Key was: " << i->mKey2);
1829 s <<
"<p class=\"notice ok\"><em>Removed:</em> " << j <<
" records</p>" << endl;
1832 Dictionary::iterator pos = mHttpParams.find(
"regAor");
1833 if (pos != mHttpParams.end() && (mHttpParams[
"action"] ==
"Add Static Registration"))
1835 Data regAor = mHttpParams[
"regAor"];
1836 Data regContact = mHttpParams[
"regContact"];
1837 Data regPath = mHttpParams[
"regPath"];
1847 const char* anchor = pb.position();
1851 pb.
data(path, anchor);
1853 if(!pb.eof()) pb.skipChar();
1854 anchor = pb.position();
1869 s <<
"<p class=\"notice ok\"><em>Added</em> permanent registered contact for: " << regAor <<
"</p>\n";
1873 s <<
"<p class=\"notice err\"><em>Error</em> adding static registration: likely database error (check logs).</p>\n";
1876 catch(resip::ParseBuffer::Exception& e)
1878 WarningLog(<<
"Registration add: aor " << regAor <<
" was malformed: " << e);
1879 s <<
"<p class=\"notice err\"><em>Error</em> parsing: AOR=" << regAor <<
"</p>\n";
1882 catch(resip::ParseBuffer::Exception& e)
1884 WarningLog(<<
"Registration add: path " << regPath <<
" was malformed: " << e);
1885 s <<
"<p class=\"notice err\"><em>Error</em> parsing: Path=" << regPath <<
"</p>\n";
1888 catch(resip::ParseBuffer::Exception& e)
1890 WarningLog(<<
"Registration add: contact " << regContact <<
" was malformed: " << e);
1891 s <<
"<p class=\"notice err\"><em>Error</em> parsing: Contact=" << regContact <<
"</p>\n";
1896 "<h2>Registrations</h2>" << endl <<
1897 "<form id=\"showReg\" method=\"get\" action=\"registrations.html\" name=\"showReg\" enctype=\"application/x-www-form-urlencoded\">" << endl <<
1901 "<h3>Add Static Registration</h3>" << endl <<
1902 "<p>A static registration is a permanent contact for an AOR: it never expires and is" << endl <<
1903 "restored on restart, so it does not need the device to send a REGISTER. Use it for" << endl <<
1904 "gateways and other endpoints that cannot register for themselves.</p>" << endl <<
1907 " <td align=\"right\">AOR:</td>" << endl <<
1908 " <td><input type=\"text\" name=\"regAor\" size=\"46\" placeholder=\"sip:alice@example.com\"/></td>" << endl <<
1911 " <td align=\"right\">Contact:</td>" << endl <<
1912 " <td><input type=\"text\" name=\"regContact\" size=\"46\" placeholder=\"<sip:alice@192.168.1.10:5060>\"/></td>" << endl <<
1915 " <td align=\"right\">Path:</td>" << endl <<
1916 " <td><input type=\"text\" name=\"regPath\" size=\"46\" placeholder=\"optional, comma separated URIs\"/></td>" << endl <<
1919 " <td></td>" << endl <<
1920 " <td align=\"right\"><input type=\"submit\" name=\"action\" value=\"Add Static Registration\"/></td>" << endl <<
1922 "</table>" << endl <<
1924 "<h3>Active Registrations</h3>" << endl <<
1927 "<thead><tr>" << endl <<
1928 " <td>AOR</td>" << endl <<
1929 " <td>Contact</td>" << endl <<
1930 " <td>User Agent</td>" << endl <<
1931 " <td>Instance ID</td>" << endl <<
1932 " <td>Reg ID</td>" << endl <<
1933 " <td>QValue</td>" << endl <<
1934 " <td>Path</td>" << endl <<
1935 " <td>Sync'd?</td>" << endl <<
1936 " <td>Expires In</td>" << endl <<
1937 " <td><input type=\"submit\" value=\"Remove\"/></td>" << endl <<
1938 "</tr></thead>" << endl <<
1944 for ( RegistrationPersistenceManager::UriList::const_iterator
1945 aorIt = aors.begin(); aorIt != aors.end(); ++aorIt )
1952 for (ContactList::iterator i = contacts.begin();
1953 i != contacts.end(); ++i )
1955 if(i->mRegExpires > now)
1957 uint64_t secondsRemaining = i->mRegExpires - now;
1966 s <<
"</td>" << endl
1975 s <<
"</td>" << endl
1982 <<
"</td><td>" << regId
1984#ifdef RESIP_FIXED_POINT
1986 s << (contact.
exists(p_q) ? contact.
param(p_q) : 1000) <<
"</td><td>";
1988 s << (contact.
exists(p_q) ? contact.
param(p_q).floatVal() : 1.0f) <<
"</td><td>";
1997 if(!staticRegContact)
1999 s <<
"</td><td>" << (r.
mSyncContact ?
"true" :
"false") <<
"</td>" << endl;
2000 s <<
"<td>" << secondsRemaining <<
"s</td>" << endl;
2004 s <<
"</td><td></td>" << endl;
2005 s <<
"</td><td>Never</td>" << endl;
2011 <<
"|" << (staticRegContact ?
"1" :
"0")
2012 <<
"\"/></td>" << endl
2023 s <<
"</tbody>" << endl <<
2024 "</table>" << endl <<
2029WebAdmin::buildPublicationsSubPage(
DataStream& s)
2031 if (!mRemoveSet.empty())
2034 for (set<RemoveKey>::iterator i = mRemoveSet.begin(); i != mRemoveSet.end(); ++i)
2040 WarningLog(<<
"Publication removal key was malformed: " << i->mKey2);
2054 WarningLog(<<
"Publication removal was unsuccessful: eventType=" << eventType <<
", docKey=" << i->mKey1 <<
", eTag=" << eTag);
2057 catch (resip::ParseBuffer::Exception& e)
2059 WarningLog(<<
"Publication removal key was malformed: " << e <<
" Key was: " << i->mKey2);
2062 s <<
"<p class=\"notice ok\"><em>Removed:</em> " << j <<
" records</p>" << endl;
2066 "<h2>Publications</h2>" << endl <<
2067 "<form id=\"showPub\" method=\"get\" action=\"publications.html\" name=\"showPub\" enctype=\"application/x-www-form-urlencoded\">" << endl <<
2068 "<div class=space>" << endl <<
2073 "<thead><tr>" << endl <<
2074 " <td>AOR</td>" << endl <<
2075 " <td>Event Type</td>" << endl <<
2076 " <td>ETag</td>" << endl <<
2077 " <td>Data</td>" << endl <<
2078 " <td>Sync'd?</td>" << endl <<
2079 " <td>Expires In</td>" << endl <<
2080 " <td><input type=\"submit\" value=\"Remove\"/></td>" << endl <<
2081 "</tr></thead>" << endl <<
2088 PublicationPersistenceManager::KeyToETagMap::iterator keyIt = publications.begin();
2089 for (; keyIt != publications.end(); keyIt++)
2093 PublicationPersistenceManager::ETagToDocumentMap::iterator eTagIt = keyIt->second.begin();
2094 for (; eTagIt != keyIt->second.end(); eTagIt++)
2096 if (eTagIt->second.mExpirationTime > now)
2098 uint64_t secondsRemaining = eTagIt->second.mExpirationTime - now;
2104 s << eTagIt->second.mDocumentKey;
2106 s <<
"</td>" << endl
2111 s << eTagIt->second.mEventType;
2114 s <<
"</td>" << endl
2116 s << eTagIt->second.mETag;
2117 s <<
"</td>" << endl
2128 s <<
"</td>" << endl;
2130 s <<
"<td>" << (eTagIt->second.mSyncPublication ?
"true" :
"false") <<
"</td>" << endl
2131 <<
"<td>" << secondsRemaining <<
"s</td>" << endl
2133 <<
"<input type=\"checkbox\" name=\"remove." << eTagIt->second.mDocumentKey <<
"\" value=\"" << eTagIt->second.mEventType <<
"|" << eTagIt->second.mETag.urlEncoded()
2134 <<
"\"/></td>" << endl
2140 s <<
"</tbody>" << endl <<
2141 "</table>" << endl <<
2148 if (mHttpParams[
"action"] ==
"Clear DNS Cache")
2153 if (mHttpParams[
"action"] ==
"Reload DNS Servers")
2158 s <<
"<h2>Logging and Admin</h2>" << endl
2159 <<
"<div class=\"toolbar\">" << endl
2160 <<
"<form id=\"logLevel\" method=\"get\" action=\"logLevel.html\" name=\"logLevel\">" << endl
2161 <<
" <label>Log level</label> <select name=\"level\">" << endl
2169 <<
" </select>" << endl
2170 <<
" <input type=\"submit\" name=\"action\" value=\"Set level\"/>" << endl
2171 <<
"</form>" << endl;
2174 s <<
"<form id=\"reloadCerts\" method=\"get\" action=\"reloadcerts.html\" name=\"reloadcerts\">" << endl
2175 <<
" <input type=\"submit\" name=\"action\" value=\"Reload Certificates\"/>" << endl
2176 <<
"</form>" << endl;
2181 s <<
"<form id=\"restartProxy\" method=\"get\" action=\"restart.html\" name=\"restart\">" << endl
2182 <<
" <input type=\"submit\" name=\"action\" value=\"Restart Proxy\"/>" << endl
2183 <<
"</form>" << endl;
2186 s <<
"</div>" << endl;
2188 s <<
"<h2>DNS Cache</h2>" << endl;
2192 Lock lock(mDnsCacheMutex);
2196 mDnsCacheCondition.wait(lock);
2197 s <<
"<pre>" << mDnsCache <<
"</pre>"
2201 s <<
"<div class=\"toolbar\">" << endl
2202 <<
"<form id=\"dnsButtons\" method=\"get\" action=\"settings.html\" name=\"dnsButtons\">" << endl
2203 <<
" <input type=\"submit\" name=\"action\" value=\"Clear DNS Cache\"/>" << endl
2204 <<
" <input type=\"submit\" name=\"action\" value=\"Reload DNS Servers\"/>" << endl
2205 <<
"</form>" << endl
2206 <<
"</div>" << endl;
2213 s <<
"<h2>Stack Info</h2>" << endl
2214 <<
"<pre>" << buffer <<
"</pre>"
2223 s <<
"<h2>Congestion Manager Statistics</h2>" << endl
2224 <<
"<pre>" << buffer <<
"</pre>"
2228 s <<
"<h2>Settings</h2>" << endl <<
2229 "<pre>" << mProxy.
getConfig() <<
"</pre>" << endl;
2235 Lock lock(mDnsCacheMutex); (void)lock;
2236 if(dnsEntryStrings.
empty())
2238 mDnsCache =
"<i>empty</i>";
2242 mDnsCache = dnsEntryStrings;
2244 mDnsCacheCondition.notify_one();
2254 const char* host =
"127.0.0.1";
2256 struct addrinfo hints = {};
2257 struct addrinfo* res =
nullptr;
2258 hints.ai_family = AF_INET;
2259 hints.ai_socktype = SOCK_STREAM;
2261 if (getaddrinfo(host,
nullptr, &hints, &res) == 0 && res !=
nullptr)
2263 struct sockaddr_in servAddr = {};
2264 memcpy(&servAddr, res->ai_addr, res->ai_addrlen);
2265 servAddr.sin_port = htons(port);
2269 sd = (int)socket(AF_INET, SOCK_STREAM, 0);
2273 struct sockaddr_in localAddr = {};
2274 localAddr.sin_family = AF_INET;
2275 localAddr.sin_addr.s_addr = htonl(INADDR_ANY);
2276 localAddr.sin_port = 0;
2277 rc = ::bind(sd, (
struct sockaddr*)&localAddr,
sizeof(localAddr));
2281 rc = ::connect(sd, (
struct sockaddr*)&servAddr,
sizeof(servAddr));
2284 Data request(
"<Restart>\r\n <Request>\r\b </Request>\r\n</Restart>\r\n");
2285 rc = send(sd, request.c_str(), (
int)request.size(), 0);
2288 s <<
"<p class=\"notice ok\"><em>Restarting proxy...</em></p>" << endl;
2297 s <<
"<p class=\"notice err\"><em>Error</em> issuing restart command.</p>" << endl;
2301 s <<
"<p class=\"notice warn\"><em>Unavailable:</em> CommandServer must be running to use the restart feature.</p>" << endl;
2308 Dictionary::iterator pos;
2311 pos = mHttpParams.find(
"level");
2312 if (pos != mHttpParams.end())
2314 newLevel = pos->second;
2315 InfoLog(<<
"new log level requested: " << newLevel);
2320 s <<
"<p class=\"notice ok\"><em>Log level changed</em> to " << newLevel.
xmlCharDataEncode() <<
".</p>" << endl;
2325 s <<
"<p class=\"notice err\"><em>Error</em> no log level specified.</p>" << endl;
2333 s <<
"<p class=\"notice ok\"><em>Reloaded certificates.</em></p>" << endl;
2337WebAdmin::buildUserPage()
2343 s <<
"<!DOCTYPE html>" << endl
2344 <<
"<html lang=\"en\">" << endl
2346 <<
"<meta charset=\"utf-8\" />" << endl
2347 <<
"<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />" << endl
2348 <<
"<title>Repro Proxy</title>" << endl
2351 <<
"</head>" << endl
2353 <<
"<div class=\"card\">" << endl
2354 <<
" <div class=\"brand\">" <<
productMarkSvg() <<
"<span class=\"name\">Repro</span></div>" << endl;
2358 s <<
"</div>" << endl
2359 <<
"</body>" << endl
2360 <<
"</html>" << endl;
2368WebAdmin::buildCertPage(
const Data& domain)
2375 ErrLog( <<
"Proxy not build with support for certificates" );
2381WebAdmin::buildDefaultPage()
2388 "<!DOCTYPE html>" << endl <<
2389 "<html lang=\"en\">" << endl <<
2391 "<meta charset=\"utf-8\" />" << endl <<
2392 "<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />" << endl <<
2393 "<title>Repro Proxy Login</title>" << endl <<
2396 "</head>" << endl <<
2399 " <div class=\"card\">" << endl <<
2400 " <div class=\"brand\">" <<
productMarkSvg() <<
"<span class=\"name\">Repro</span></div>" << endl <<
2401 " <div class=\"sub\">SIP Proxy · Admin Console</div>" << endl <<
2402 " <a class=\"cta\" href=\"user.html\">Log in</a>" << endl <<
2403 " <p>Note: Web admin accounts are stored in a file (default filename is users.txt). "
2404 "You can create it with the <a href=\"https://httpd.apache.org/docs/current/programs/htdigest.html\">htdigest</a> utility.</p>" << endl <<
2405 " </div>" << endl <<
2406 "</body>" << endl <<
2418 setPage(jsonBody, pageNumber, statusCode,
Mime(
"application",
"json"));
2430 Lock lock(mStatsMutex); (void)lock;
2431 statsMessage.
loadOut(mStatsPayload);
2434 mStatsCondition.notify_all();
#define WarningLog(args_)
#define resip_assert(x)
This file needs to work for both C and C++ code.
Handles cross-platform sockets compatibility.
resip::Data mCondition1Header
resip::Data mCondition2Regex
resip::Data mCondition2Header
resip::Data mCondition1Regex
resip::Data mRewriteExpression
resip::Data mMatchingPattern
resip::Data passwordHashAlt
void eraseAcl(const resip::Data &tlsPeerName, const resip::Data &address, const short &mask, const short &port, const short &family, const short &transport)
resip::Data getTlsPeerName(const resip::Data &key)
short getAddressMask(const resip::Data &key)
Key getNextTlsPeerNameKey(Key &key)
Key getFirstTlsPeerNameKey()
Key getNextAddressKey(Key &key)
resip::Tuple getAddressTuple(const resip::Data &key)
bool addAcl(const resip::Data &tlsPeerName, const resip::Data &address, const short &mask, const short &port, const short &family, const short &transport)
bool addDomain(const resip::Data &domain, const int tlsPort)
std::map< resip::Data, AbstractDb::ConfigRecord > ConfigData
void eraseDomain(const resip::Data &domain)
const ConfigData & getConfigs() const
bool updateFilter(const resip::Data &originalKey, const resip::Data &cond1Header, const resip::Data &cond1Regex, const resip::Data &cond2Header, const resip::Data &cond2Regex, const resip::Data &method, const resip::Data &event, short action, const resip::Data &actionData, const short order)
bool addFilter(const resip::Data &cond1Header, const resip::Data &cond1Regex, const resip::Data &cond2Header, const resip::Data &cond2Regex, const resip::Data &method, const resip::Data &event, short action, const resip::Data &actionData, const short order)
void eraseFilter(const resip::Data &cond1Header, const resip::Data &cond1Regex, const resip::Data &cond2Header, const resip::Data &cond2Regex, const resip::Data &method, const resip::Data &event)
bool test(const resip::Data &cond1Header, const resip::Data &cond2Header, short &action, resip::Data &actionData)
AbstractDb::FilterRecord getFilterRecord(const resip::Data &key)
void setPage(const resip::Data &page, int pageNumber, int response=200, const resip::Mime &pType=resip::Mime("text","html"))
ProxyConfig & getConfig() noexcept
resip::SipStack & getStack() noexcept
std::vector< resip::Uri > UriList
UriList process(const resip::Uri &ruri, const resip::Data &method, const resip::Data &event)
bool addRoute(const resip::Data &method, const resip::Data &event, const resip::Data &matchingPattern, const resip::Data &rewriteExpression, const short order)
AbstractDb::RouteRecord getRouteRecord(const resip::Data &key)
void eraseRoute(const resip::Data &method, const resip::Data &event, const resip::Data &matchingPattern, const short order)
bool updateRoute(const resip::Data &originalKey, const resip::Data &method, const resip::Data &event, const resip::Data &matchingPattern, const resip::Data &rewriteExpression, const short order)
bool addStaticReg(const resip::Uri &aor, const resip::NameAddr &contact, const resip::NameAddrs &path)
void eraseStaticReg(const resip::Uri &aor, const resip::NameAddr &contact)
StaticRegStore mStaticRegStore
bool addUser(const resip::Data &user, const resip::Data &domain, const resip::Data &realm, const resip::Data &password, bool applyA1HashToPassword, const resip::Data &fullName, const resip::Data &emailAddress, const resip::Data &passwordHashAlt=resip::Data::Empty)
void eraseUser(const Key &key)
bool updateUser(const Key &originalKey, const resip::Data &user, const resip::Data &domain, const resip::Data &realm, const resip::Data &password, bool applyA1HashToPassword, const resip::Data &fullName, const resip::Data &emailAddress, const resip::Data &passwordHashAlt=resip::Data::Empty)
AbstractDb::UserRecord getUserInfo(const Key &key) const
static const VersionUtils & instance()
virtual void handleStatisticsMessage(resip::StatisticsMessage &statsMessage)
virtual void onDnsCacheDumpRetrieved(std::pair< unsigned long, unsigned long > key, const resip::Data &dnsEntryStrings)
void parseUserFile()
Load web admin users from a file.
void setApiResponse(int pageNumber, int statusCode, const resip::Data &jsonBody)
WebAdmin(Proxy &proxy, resip::RegistrationPersistenceManager ®Db, resip::PublicationPersistenceManager &pubDb, const resip::Data &realm, int port=5080, resip::IpVersion version=resip::V4, const resip::Data &ipAddr=resip::Data::Empty)
virtual void buildPage(const resip::Data &method, const resip::Data &uri, int pageNumber, const resip::Data &user, const resip::Data &password)
The abstract base-class for all exceptions thrown by resip code.
Data getDomainCertDER(const Data &domainName) const
unsigned short getConfigUnsignedShort(const resip::Data &name, int defaultValue) const
virtual EncodeStream & encodeCurrentState(EncodeStream &strm) const =0
Get the current state of all monitored fifos.to a stream.
An iostream that operates on an existing Data.
An alternative to std::string, encapsulates an arbitrary buffer of bytes.
static const Data Empty
Constant that represents a zero-length data.
const char * c_str() const
Returns a null-terminated string representing.
int replace(const Data &match, const Data &target, int max=INT_MAX)
Replaces up to max occurrences of the bytes match with target.
Data xmlCharDataEncode() const
Performs in-place XML Character Data escaping of a Data.
bool prefix(const Data &pre) const noexcept
Returns true if this Data starts with the bytes indicated by the passed-in Data.
Data substr(size_type first, size_type count=Data::npos) const
Copies a portion of this Data into a new Data.
bool empty() const noexcept
Checks whether the Data is empty.
Data urlDecoded() const
Performs in-place HTTP URL un-escaping of a Data.
size_type size() const noexcept
Returns the number of bytes in this Data.
EncodeStream & urlEncode(EncodeStream &s) const
Escapes a Data to a stream according to HTTP URL encoding rules.
static Data from(const T &x)
Converts from arbitrary other type to Data.
static const size_type npos
Represents an impossible position; returned to indicate failure to find.
Data urlEncoded() const
Performs in-place HTTP URL escaping of a Data.
const char * data() const noexcept
Returns a pointer to the contents of this Data.
Used to accumlate data written to the stream in a DigestBuffer and convert the data to Digest.
Data getHex()
Calls flush() on itself and returns the Digest data in hex format.
SIP body type for holding PIDF contents (MIME content-type application/pidf+xml).
const bool getSimplePresenceOnline()
const Data & getSimplePresenceNote()
static Level toLevel(const Data &l)
static void setLevel(Level level)
Set logging level for current thread.
static Level level()
Return logging level for current thread.
Represents the "media-type" element in the RFC 3261 grammar.
Represents the "name-addr" and the "addr-spec" elements in the RFC 3261 grammar.
bool exists(const Param< NameAddr > ¶mType) const
const Data & param(const ExtensionParameter ¶m) const
Const accessor for non-natively-supported parameter types.
Provides string-parsing functionality with protection from buffer overflow.
CurrentPosition skipToEnd()
CurrentPosition skipWhitespace()
CurrentPosition skipToChar(char c)
CurrentPosition position() const
CurrentPosition skipToOneOf(const char *cs)
CurrentPosition skipChar()
void data(Data &data, const char *start) const
make the passed in data share memory with the buffer (uses Data::Share)
BaseException used to indicate parse failures.
iterator end()
Returns an iterator pointing to the last header field value.
iterator begin()
Returns an iterator pointing to the first header field value.
void push_back(const T &t)
Inserts a header field value at the back of this container.
Abstract interface of a datastore of all publication documents processed by DUM.
virtual void unlockDocuments()=0
std::map< resip::Data, ETagToDocumentMap > KeyToETagMap
virtual KeyToETagMap & getDocuments()=0
virtual void lockDocuments()=0
virtual bool removeDocument(const Data &eventType, const Data &documentKey, const Data &eTag, uint64_t lastUpdated, bool syncPublication=false)=0
Abstract interface of a datastore of all registered endpoints processed by DUM.
virtual update_status_t updateContact(const Uri &aor, const ContactInstanceRecord &rec)=0
virtual void removeContact(const Uri &aor, const ContactInstanceRecord &rec)=0
virtual void getContacts(const Uri &aor, ContactList &container)=0
virtual void getAors(UriList &container)=0
Security * getSecurity() const
Returns a pointer to the embedded Security object, 0 if not set.
EncodeStream & dump(EncodeStream &strm) const
output current state of the stack - for debug
void clearDnsCache()
Clear the DNS Cache.
void getDnsCacheDump(std::pair< unsigned long, unsigned long > key, GetDnsCacheDumpHandler *handler)
Get a string representation of the DNS Cache.
void reloadCertificates()
reload the TLS X.509 certificates
void reloadDnsServers()
Check if DnsServers list has changed, and if so reinitializes the DNS resolver (ares).
void loadOut(Payload &payload) const
static const char * COLON
static const char * COMMA
static uint64_t getTimeSecs()
Returns the current clock time in seconds.
Data presentationFormat() const
Wrapper around the inet_top() method.
static const Data & toData(TransportType)
Converts the TransportType to a string representation of the transport type, e.g.
TransportType getType() const
static TransportType toTransport(const Data &)
Converts a string representation of transport type, i.e.
Represents the "SIP-URI" and "SIPS-URI" elements in the RFC 3261 grammar.
CongestionManager * getCongestionManager()
Accessor for the CongestionManager object the stack is using.
This Class is used to implement a primitive form of RPC using loose XML formatting.
dcm! – add UnusedChecking(_enum) below;
static const uint64_t NeverExpire
std::unique_lock< Mutex > Lock
A convenience class to lock a Lockable (such as a Mutex) on object creation, and unlock on destructio...
int closeSocket(Socket fd)
TransportType
An enumeration of transport protocols.
IpVersion
An enumeration of IP versions.
std::list< ContactInstanceRecord > ContactList
static const char * faviconLink()
#define REPRO_BORDERLESS_TABLE_PROPS
#define REPRO_BORDERED_TABLE_PROPS
#define REPRO_DEFAULT_COMMAND_PORT
static const char * standaloneStyle()
static const char * productMarkSvg()
unsigned __int64 uint64_t