Coverage Report

Created: 2026-02-14 06:56

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libsoup/libsoup/auth/soup-connection-auth.c
Line
Count
Source
1
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */
2
/*
3
 * soup-connection-auth.c: Abstract base class for hacky Microsoft
4
 * connection-based auth mechanisms (NTLM and Negotiate)
5
 *
6
 * Copyright (C) 2010 Red Hat, Inc.
7
 */
8
9
#ifdef HAVE_CONFIG_H
10
#include <config.h>
11
#endif
12
13
#include <ctype.h>
14
#include <string.h>
15
16
#include "soup-connection-auth.h"
17
#include "soup.h"
18
#include "soup-connection.h"
19
#include "soup-message-private.h"
20
21
typedef struct {
22
  GHashTable *conns;
23
  GMutex lock;
24
} SoupConnectionAuthPrivate;
25
26
0
G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (SoupConnectionAuth, soup_connection_auth, SOUP_TYPE_AUTH)
27
0
28
0
static void
29
0
soup_connection_auth_init (SoupConnectionAuth *auth)
30
0
{
31
0
  SoupConnectionAuthPrivate *priv = soup_connection_auth_get_instance_private (auth);
32
33
0
  g_mutex_init (&priv->lock);
34
0
  priv->conns = g_hash_table_new (NULL, NULL);
35
0
}
36
37
static void connection_disconnected (SoupConnection *conn, gpointer user_data);
38
39
static void
40
soup_connection_auth_free_connection_state (SoupConnectionAuth *auth,
41
              SoupConnection     *conn,
42
              gpointer            state)
43
0
{
44
0
  g_signal_handlers_disconnect_by_func (conn, G_CALLBACK (connection_disconnected), auth);
45
0
  SOUP_CONNECTION_AUTH_GET_CLASS (auth)->free_connection_state (auth, state);
46
0
}
47
48
static void
49
connection_disconnected (SoupConnection *conn, gpointer user_data)
50
0
{
51
0
  SoupConnectionAuth *auth = user_data;
52
0
        SoupConnectionAuthPrivate *priv = soup_connection_auth_get_instance_private (auth);
53
0
  gpointer state;
54
55
0
  g_mutex_lock (&priv->lock);
56
0
  state = g_hash_table_lookup (priv->conns, conn);
57
0
  g_hash_table_remove (priv->conns, conn);
58
0
  g_mutex_unlock (&priv->lock);
59
0
  soup_connection_auth_free_connection_state (auth, conn, state);
60
0
}
61
62
static void
63
soup_connection_auth_finalize (GObject *object)
64
0
{
65
0
  SoupConnectionAuth *auth = SOUP_CONNECTION_AUTH (object);
66
0
        SoupConnectionAuthPrivate *priv = soup_connection_auth_get_instance_private (auth);
67
0
  GHashTableIter iter;
68
0
  gpointer conn, state;
69
70
0
  g_mutex_lock (&priv->lock);
71
0
  g_hash_table_iter_init (&iter, priv->conns);
72
0
  while (g_hash_table_iter_next (&iter, &conn, &state)) {
73
0
    soup_connection_auth_free_connection_state (auth, conn, state);
74
0
    g_hash_table_iter_remove (&iter);
75
0
  }
76
0
  g_hash_table_destroy (priv->conns);
77
0
  g_mutex_unlock (&priv->lock);
78
0
  g_mutex_clear (&priv->lock);
79
80
0
  G_OBJECT_CLASS (soup_connection_auth_parent_class)->finalize (object);
81
0
}
82
83
84
/**
85
 * soup_connection_auth_get_connection_state_for_message:
86
 * @auth: a #SoupConnectionAuth
87
 * @msg: a #SoupMessage
88
 *
89
 * Returns an associated connection state object for the given @auth and @msg.
90
 *
91
 * This function is only useful from within implementations of SoupConnectionAuth
92
 * subclasses.
93
 *
94
 * Returns: (transfer none): the connection state
95
 *
96
 **/
97
gpointer
98
soup_connection_auth_get_connection_state_for_message (SoupConnectionAuth *auth,
99
                   SoupMessage *msg)
100
0
{
101
0
        SoupConnectionAuthPrivate *priv = soup_connection_auth_get_instance_private (auth);
102
0
  SoupConnection *conn;
103
0
  gpointer state;
104
105
0
  g_return_val_if_fail (SOUP_IS_CONNECTION_AUTH (auth), NULL);
106
0
  g_return_val_if_fail (SOUP_IS_MESSAGE (msg), NULL);
107
108
0
  conn = soup_message_get_connection (msg);
109
0
  g_mutex_lock (&priv->lock);
110
0
  state = g_hash_table_lookup (priv->conns, conn);
111
0
  if (!state) {
112
0
                state = SOUP_CONNECTION_AUTH_GET_CLASS (auth)->create_connection_state (auth);
113
0
                g_hash_table_insert (priv->conns, conn, state);
114
0
                g_mutex_unlock (&priv->lock);
115
0
                if (conn) {
116
0
                        g_signal_connect_object (conn, "disconnected",
117
0
                                                 G_CALLBACK (connection_disconnected), auth, 0);
118
0
                }
119
0
        } else {
120
0
                g_mutex_unlock (&priv->lock);
121
0
  }
122
0
        g_clear_object (&conn);
123
124
0
  return state;
125
0
}
126
127
static gboolean
128
soup_connection_auth_update (SoupAuth    *auth,
129
           SoupMessage *msg,
130
           GHashTable  *auth_params)
131
0
{
132
0
  SoupConnectionAuth *cauth = SOUP_CONNECTION_AUTH (auth);
133
0
  gpointer conn = soup_connection_auth_get_connection_state_for_message (cauth, msg);
134
0
  GHashTableIter iter;
135
0
  GString *auth_header;
136
0
  gpointer key, value;
137
0
  gboolean result;
138
139
  /* Recreate @auth_header out of @auth_params. If the
140
   * base64 data ended with 1 or more "="s, then it
141
   * will have been parsed as key=value. Otherwise
142
   * it will all have been parsed as key, and value
143
   * will be %NULL.
144
   */
145
0
  auth_header = g_string_new (soup_auth_get_scheme_name (auth));
146
0
  g_hash_table_iter_init (&iter, auth_params);
147
0
  if (g_hash_table_iter_next (&iter, &key, &value)) {
148
0
    if (value) {
149
0
      g_string_append_printf (auth_header, " %s=%s",
150
0
            (char *)key,
151
0
            (char *)value);
152
0
    } else {
153
0
      g_string_append_printf (auth_header, " %s",
154
0
            (char *)key);
155
0
    }
156
157
0
    if (g_hash_table_iter_next (&iter, &key, &value)) {
158
0
      g_string_free (auth_header, TRUE);
159
0
      return FALSE;
160
0
    }
161
0
  }
162
163
0
  result = SOUP_CONNECTION_AUTH_GET_CLASS (auth)->
164
0
    update_connection (cauth, msg, auth_header->str, conn);
165
166
0
  g_string_free (auth_header, TRUE);
167
0
  return result;
168
0
}
169
170
static char *
171
soup_connection_auth_get_authorization (SoupAuth    *auth,
172
          SoupMessage *msg)
173
0
{
174
0
  SoupConnectionAuth *cauth = SOUP_CONNECTION_AUTH (auth);
175
0
  gpointer conn = soup_connection_auth_get_connection_state_for_message (cauth, msg);
176
177
0
  return SOUP_CONNECTION_AUTH_GET_CLASS (auth)->
178
0
    get_connection_authorization (cauth, msg, conn);
179
0
}
180
181
static gboolean
182
soup_connection_auth_is_ready (SoupAuth    *auth,
183
             SoupMessage *msg)
184
0
{
185
0
  SoupConnectionAuth *cauth = SOUP_CONNECTION_AUTH (auth);
186
0
  gpointer conn = soup_connection_auth_get_connection_state_for_message (cauth, msg);
187
188
0
  return SOUP_CONNECTION_AUTH_GET_CLASS (auth)->
189
0
    is_connection_ready (SOUP_CONNECTION_AUTH (auth), msg, conn);
190
0
}
191
192
static void
193
soup_connection_auth_class_init (SoupConnectionAuthClass *connauth_class)
194
0
{
195
0
  SoupAuthClass *auth_class = SOUP_AUTH_CLASS (connauth_class);
196
0
  GObjectClass *object_class = G_OBJECT_CLASS (connauth_class);
197
198
0
  auth_class->update = soup_connection_auth_update;
199
0
  auth_class->get_authorization = soup_connection_auth_get_authorization;
200
0
  auth_class->is_ready = soup_connection_auth_is_ready;
201
202
0
  object_class->finalize = soup_connection_auth_finalize;
203
0
}